LINUX.ORG.RU

Ошибка «This field is required.», при попытке загрузить изображение из формы.

 , ,


0

2

При попытке загрузить изображение из формы, возникает ошибка: {«errors»: [{«img»: [«This field is required.»]}]}

Ходя я добавляю файл в форму через кнопку загрузить и он там отображается. Но при нажатии кнопки «submit» выходит страница с ошибкой ,что форма должна быть обязательно заполнена.

models.py

class Picture(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    img = models.ImageField(upload_to = 'uploads')
    name_image = models.CharField(max_length=30, verbose_name='Название изображения', blank=True, null=True)
    upload_time = models.DateTimeField(
        verbose_name='Время загрузки', blank=True, null=True)

views.py

class UploadImage(TemplateView):
    template_name = 'resize_image/upload_image.html'
    success_url = reverse_lazy('image_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['upload_pic_form'] = UploadPictureForm(
            self.request.POST, self.request.FILES )
        return context

    def post(self, request, *args, **kwargs):
        context = self.get_context_data()
        if context['upload_pic_form'].is_valid():
            pic = context['upload_pic_form'].save(commit=False)
            pic.save()

            return redirect('image_list')
        return JsonResponse({'errors': [context['upload_pic_form'].errors]})

urls.py

urlpatterns = [
    path('', views.image_list, name='image_list'),
    path('upload/', views.UploadImage.as_view(), name='upload_image'),
    
]

HTML страница с формой:

{% extends 'resize_image/base.html' %} 
{% load staticfiles %}
 {% block content %}
 <form action="" method="post">
     {% csrf_token %}
     <div class="form-group">
            <label>файл</label>
            {{ upload_pic_form.img }}
        </div>
        <div class="form-group">
                <label>Название</label>
                {{ upload_pic_form.name_image }}
            </div>
    <input type="submit" value="Submit" />
</form>
        
 {% endblock %}

Мне ,кажется, что get context у меня лишний в этой форме. Подозреваю ,что ошибка во views.py, но где именно не пойму.

-- Подскажите, пожалуйста ,как правильно реализовать загрузку изображения, чтоб не возникало ошибок.

-- И как можно реализовать одновременно два варианта загрузки - и из стандартной формы выбора файла и из строки url.

как правильно реализовать загрузку изображения, чтоб не возникало ошибок

Хорошенько обмазаться js и грузить картинку post/put запросом с сырыми данными картинки, без б-гомерзкого multipart

makoven ★★★★★ ()
<form action="" method="post" enctype="multipart/form-data">

Но это действительно «привет 90-е», модные пацаны сейчас юзают ЖС.

provaton ★★★★★ ()
Последнее исправление: provaton (всего исправлений: 1)
Ответ на: комментарий от provaton

Да мне хотя бы , чтоб без мультипарта загружалось. Просто загрузка выбором одного файла и вторая форма - загрузка по url.

Это реально сделать без ЖС ?

PavelShturm ()
Ответ на: комментарий от provaton

Спасибо! С enctype=«multipart/form-data» - все начало загружаться. А я его специально стер, не разобравшись... Думал он для загрузки разом кучи файлов))

PavelShturm ()
Ответ на: комментарий от provaton

А чтоб по url изображения загружать, не нужно мультипарт использовать? Там другую форму ввода делать надо, не стандартную?

PavelShturm ()
Ответ на: комментарий от PavelShturm

Мультипарт нужен тогда и только тогда, когда в форме есть поля для аплоада файлов. Если у вас форма с полем для ввода УРЛ, то соответственно мультипарт не нужен.

provaton ★★★★★ ()
Ответ на: комментарий от provaton

В моем случае, чтоб по url загрузить. Мне только в шаблоне новую форму ввода реализовать надо или дополнительно во вьюхе для загрузки по url тоже надо?

PavelShturm ()
Ответ на: комментарий от anonymous

Он не плох. Посто не JSON - не модно)

В youtube API, кажется сделано так, что сначала отправляешь JSON с описанием файла. Он тебе в ответ ссылку на ресурс, на который затем надо сделать POST с твоим видеофайлом в теле запроса

makoven ★★★★★ ()
Ответ на: комментарий от makoven

Подскажите ,пожалуйста, как через введенный в форму url закачивать картинку. Что-то я не нахожу примеров в инете((

Есть ли вариант обойтись без js?

PavelShturm ()
Ответ на: комментарий от makoven

Спасибо! основную суть понял.

А как можно получить в контексте значение из формы в шаблоне, не привязанное к модели? Чтоб в модели не пришлось создавать поле для урл.

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['pic_url'] = '' # так можно пустое поле формы сделать? 
        context['upload_pic_form'] = UploadPictureForm(
            self.request.POST, self.request.FILES )
        return context

    def post(self, request, *args, **kwargs):
        context = self.get_context_data()
        if context['pic_url'].is_valid():
            pic_url = context['pic_url']
            name = urlparse(pic_url).path.split('/')[-1]
            picture = Picture()
            response = requests.get(pic_url)
            if response.status_code == 200:
                picture.img = ContentFile(response.content)
                picture.upload_time = timezone.now()
                picture.name_image = name
                picture.save()

        if context['upload_pic_form'].is_valid():
            pic = context['upload_pic_form'].save(commit=False)
            pic.save()

Такое примерно должно получиться? Подскажите,пожалуйста, в чем я не прав.

PavelShturm ()
Ответ на: комментарий от PavelShturm

Такое примерно должно получиться? Подскажите,пожалуйста, в чем я не прав.

Нет, так нельзя сделать пустое поле, кто тебе такую хуйню показал?! И используй form_valid. И если тебе так припёрло использовать class based views, то прочитай их сорцы. Или используй тупо функции.

class MyView(FormView):
    form_class = UploadPictureForm

    def form_valid(self, form):
        # делай со своей формой что тебе надо
        return HttpResponse()

И не слушай makoven, он на днях перешёл с жабоскрипта на питон, у него переодически происходят рецидивы. Он ради формы из двух полей будет три дня пилить обвязку на жабоскрипте.

anonymous ()
Ответ на: комментарий от anonymous

Сделал вот так:

class UploadImage(CreateView):
    form_class = UploadPictureForm
    template_name = 'resize_image/upload_image.html'
    success_url = reverse_lazy('image_list')

    def form_valid(self, form):
        Picture.objects.create(**form.cleaned_data)
        return redirect(self.get_success_url())

Теперь при сохранении файла, начала появляться ошибка:

url = self.success_url.format(**self.object.__dict__)
AttributeError: 'NoneType' object has no attribute '__dict__'

И еще вопросик ,можно ли производить запись вот в таком формате?

def form_valid(self, form):
    Picture.objects.create(
        upload_time = timezone.now(),
        img = form.img,
        name_image = form.name_image,
        )
    return redirect(self.get_success_url())

И все-равно пока не понятно, как получить строку с УРЛ, чтоб обработать ее и получить картинку. Ведь из форм приходят готовые поля и там нет поля под УРЛ.

PavelShturm ()
Ответ на: комментарий от PavelShturm

Сделал вот так:

Нахуя ты так сделал?

Сделай так:

class UploadImage(CreateView):
    form_class = UploadPictureForm
    template_name = 'resize_image/upload_image.html'
    success_url = reverse_lazy('image_list')
    model = Picture

Или, блджад, загляни наконец в сорскод тех вещей, которые ты пытаешься переписать и сделай так

class UploadImage(CreateView):
    form_class = UploadPictureForm
    template_name = 'resize_image/upload_image.html'
    success_url = reverse_lazy('image_list')

    def form_valid(self, form):
        self.object = Picture.objects.create(**form.cleaned_data)
        return redirect(self.get_success_url())

И еще вопросик ,можно ли производить запись вот в таком формате?

Опять документацию не читал? Нельзя, данные из формы положено вынимать из form.cleaned_data.

И все-равно пока не понятно, как получить строку с УРЛ, чтоб обработать ее и получить картинку. Ведь из форм приходят готовые поля и там нет поля под УРЛ.

Если ты хочешь добавить в форму поле под урл, то так и сделай.

class UploadPictureForm(forms.Form):
    url = forms.URLField(required=False)
    file = forms.FileField(required=False)

    def clean(self):
        url = self.cleaned_data['url']
        file = self.cleaned_data['file']

        if (url and file) or (not url and not file):
            raise forms.ValidationError('херово заполнил')

        return self.cleaned_data

Или тупо две раздельных формы запили.

anonymous ()
Ответ на: комментарий от anonymous

Крайне извиняюсь за свою тупость и очень благодарен за ваши советы!

# forms.py
class UploadPictureForm(forms.Form):
    
    name_image = forms.CharField(max_length=30)
    url = forms.URLField(required=False)
    img = forms.ImageField(required=False)

    def clean(self):
        url = self.cleaned_data['url']
        img = self.cleaned_data['img']

        if (url and img) or (not url and not img):
            raise forms.ValidationError('херово заполнил')

        return self.cleaned_data

# views.py
class UploadImage(CreateView):
    form_class = UploadPictureForm
    template_name = 'resize_image/upload_image.html'
    success_url = reverse_lazy('image_list')
    
    def form_valid(self, form):
        self.object = Picture.objects.create(
            upload_time = timezone.now(),
            **form.cleaned_data)
        return redirect(self.success_url)

Но такой вариант формы выдает ошибку, с предыдущим все начало работать(без «self.object =» тоже работает.):

__init__() got an unexpected keyword argument 'instance'
PavelShturm ()
Ответ на: комментарий от anonymous

Спасибо. Заметил,что нужно было или форму от ModelForm наследовать или вьюху от FormViews, тогда страница с формой начинала формироваться.

Теперь при попытке загрузить картинку (выбором из папки, оставив поле УРЛ пустым) выдает ошибку:

'url' is an invalid keyword argument for this function

Variable	Value
form	
<UploadPictureForm bound=True, valid=True, fields=(img;name_image;url)>
self	
<resize_image.views.UploadImage object at 0x7fc813b239e8>

PavelShturm ()
Ответ на: комментарий от PavelShturm

Переделал вьюху: Теперь если УРЛ, пустой, то нормально записывает файл.

def form_valid(self, form):
        
        self.object = Picture.objects.create(
            upload_time = timezone.now(),
            img = form.cleaned_data['img'],
            name_image = form.cleaned_data['name_image'],)
        return redirect(self.success_url)

Но если выбрать файл и ввести урл, то почему-то не выводит сообщение ('херово заполнил'):

def clean(self):
        url = self.cleaned_data['url']
        img = self.cleaned_data['img']

        if (url and img) or (not url and not img):
            raise forms.ValidationError('херово заполнил')

        return self.cleaned_data

PavelShturm ()
Ответ на: комментарий от anonymous

Я ,наверно, изобретаю велосипед и правильно сделать гораздо проще:

def form_valid(self, form):
        if form.cleaned_data['img'] and not form.cleaned_data['url']:
            self.object = Picture.objects.create(
                upload_time = timezone.now(),
                img = form.cleaned_data['img'],
                name_image = form.cleaned_data['name_image'],)

        if form.cleaned_data['url'] and not form.cleaned_data['img']:
            pic_url = form.cleaned_data['url']
            name = urlparse(pic_url).path.split('/')[-1]
            response = requests.get(pic_url)
            if response.status_code == 200:
                self.object = Picture.objects.create(
                    upload_time = timezone.now(),
                    img = ContentFile(response.content),
                    name_image = name,)

        return redirect(self.success_url)

Первое условие срабатывает нормально. А вот со вторым возникает проблемка - «img = ContentFile(response.content)», не передает изображение.(создается запись с названием и датой, но без изображения.)

Я не правильно получаю изображение с помощью «requests.get(pic_url)» ?

PavelShturm ()
Ответ на: комментарий от PavelShturm

Переделал вьюху , теперь все загружается, но почему-то через первое условие изображения сжимаются при загрузке,а при втором условии идут в оригинальном размере:

def form_valid(self, form):
        if form.cleaned_data['img'] and not form.cleaned_data['url']:
            self.object = Picture.objects.create(
                upload_time = timezone.now(),
                img = form.cleaned_data['img'],
                name_image = form.cleaned_data['name_image'],)

        if form.cleaned_data['url'] and not form.cleaned_data['img']:
            pic_url = form.cleaned_data['url']
            name = urlparse(pic_url).path.split('/')[-1]
            response = requests.get(pic_url)
            picture = Picture()
            if response.status_code == 200:
                picture.img.save(name, ContentFile(response.content), save=False)
                picture.upload_time = timezone.now()
                picture.name_image = name
                picture.save()
                
        return redirect(self.success_url)

Можно ли сделать, чтоб изображения приходили одного размера?

PavelShturm ()
Ответ на: комментарий от PavelShturm

Ничего не должно по умолчанию сжиматься. Ты о размере файла или о широте/высоте картинки?

Выложи уже свой код куда-нибудь.

anonymous ()
Ответ на: комментарий от PavelShturm

Написал себе пример, у меня всё грузится одинаково. Делал через FormView (ты CreateView не используешь толково, оно тебе только мешает) и forms.Form.

anonymous ()
Ответ на: комментарий от anonymous

Я ,наверно, не правильно выразился. я имел ввиду, что при выводе картинок списком из бд, картинки загруженные из файла имеют примерно одинаковые размеры(не большие), а картинки полученные по УРЛ на много больше внешне.(широту/высоту, ну как следствие размер файла в 10 раз отличается)

Выложил:

https://github.com/PavelShturm85/catalog_image

PavelShturm ()
Ответ на: комментарий от anonymous

Спасибо за Вашу помощь! +100500 Вам к карме!)

Подскажите, где я накосячил с :

def clean(self):
        url = self.cleaned_data['url']
        img = self.cleaned_data['img']

        if (url and img) or (not url and not img):
            raise forms.ValidationError('херово заполнил')

        return self.cleaned_data

Или я не правильно понял суть работы этой функции и она не должна сообщение выводить , если выбран файл и введен УРЛ или при отсутствии обоих?

PavelShturm ()
Ответ на: комментарий от PavelShturm
def clean(self):
        # так не ошибёшься, значений url или img в cleaned_data может не быть
        cleaned_data = super().clean()

        url = self.cleaned_data.get('url')
        img = self.cleaned_data.get('img')

        if (url and img) or (not url and not img):
            raise forms.ValidationError('херово заполнил')

        return self.cleaned_data

В upload_image.html добавь

{{ form.non_field_errors }}

anonymous ()
Ответ на: комментарий от anonymous

Спасибо! теперь сообщение о валидации выходит.

Не пробовали изображения загружать по УРЛ и из файла. Разницу в размерах не заметили?

PavelShturm ()
Ответ на: комментарий от PavelShturm

Изображения разного размера были, т.к. я их скачивал из интернета в размере меньше оригинального. А так они одинаково приходят в базу.

PavelShturm ()
Ответ на: комментарий от anonymous

Подскажите,пожалуйста, а как через FormView, получить контекст изображения по <pk>. Используя TemlateView - все нормально срабатывает, а при FormView выдает ошибку :

get_context_data() missing 1 required positional argument: 'pk'

# forms.py
class EditPictureForm(forms.Form):

    name_image = forms.CharField(max_length=30, required=False)
    image_w = forms.IntegerField(required=False)
    image_h = forms.IntegerField(required=False)
    image_quality = forms.IntegerField(required=False)

    def clean(self):
        cleaned_data = super().clean()
        image_w = cleaned_data.get('image_w')
        image_h = cleaned_data.get('image_h')
        image_quality = cleaned_data.get('image_quality')
        
        if image_w < 0 or image_h < 0:
            raise forms.ValidationError(
                'Размер должен быть положительным числом')

        elif image_quality < 1 or image_quality > 99:
            raise forms.ValidationError(
                'Качество изображения должно быть числом от 1 до 99')

        return cleaned_data

# views.py
class EditImage(FormView):
    template_name = 'resize_image/edit_image.html'
    form_class = EditPictureForm
    success_url = reverse_lazy('image_list')
    
    def get_context_data(self, pk, **kwargs):
        context = super().get_context_data(**kwargs)
        context['image'] = get_object_or_404(Picture, pk=pk)
        
        return context

Я хочу вывести на страницу изображение и форму для редактирования его размеров.

{% extends 'resize_image/base.html' %} 
{% load staticfiles %}
 {% block content %}
 <form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    
   <div class="form-group">
       <h4>Изменение изображения</h4>
       <label>Название изображения</label>
       {{ form.name_image }}
   </div>
   <div class="form-group">
       <label>Ширина изображения</label>
       {{ form.image_w }}
   </div>
   <br>
   <div class="form-group">
       <label>Высота изображения</label>
       {{ form.image_h }}
   </div>    
   <div class="form-group">
       <label>Качество изображения от 1 до 99</label>
       {{ form.image_quality }}
   </div>  
   <div style="color: red;">{{ form.non_field_errors }}</div>
    <input type="submit" value="Submit" />       
   
</form>
<br>
 <img src="{{image.img.url}}">
        
 {% endblock %}
PavelShturm ()
Ответ на: комментарий от PavelShturm

Вот так начало работать:

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['image'] = get_object_or_404(Picture, pk=self.kwargs['pk'])

PavelShturm ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.