rahul
rahul

Reputation: 366

Setting form fields in django class based generic view CreateView

I'm using django's CreateView to add images to a book. I pass the book's id to the class based view as a parameter in the url. Form fields such as book and language are not rendered on the template, rather they're obtained with the help of the book's id.

# views.py
class PictureCreateView(CreateView):
    model = Upload
    fields = "__all__"
    book_id = None

    def get_initial(self):
        initial = super(PictureCreateView, self).get_initial()
        initial = initial.copy()
        self.book_id = self.kwargs['book_id']
        book = Book.objects.get(id=self.book_id)
        initial['book'] = book
        initial['language'] = language
        initial['uploader'] = self.request.user
        return initial

    # set book_id so it used in the template
    def get_context_data(self, **kwargs):
        context = super(PictureCreateView, self).get_context_data(**kwargs)
        context['book_id'] = self.book_id
        return context

    def form_valid(self, form, **kwargs):
        print('Form is valid')
        self.object = form.save()
        files = [serialize(self.object)]
        data = {'files': files}
        response = JSONResponse(data, mimetype=response_mimetype(self.request))
        response['Content-Disposition'] = 'inline; filename=files.json'
        return super(PictureCreateView, self).form_valid(form)

    def form_invalid(self, form):
        print('Form invalid!')
        print(form.errors)
        data = json.dumps(form.errors)
        return HttpResponse(content=data, status=400, content_type='application/json')

# models.py
class Upload(models.Model):
    image = models.ImageField(upload_to=get_upload_path, help_text='Image to process')
    uploader = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name='uploader')
    language = models.ForeignKey(Language, models.CASCADE)
    book = models.ForeignKey(Book, models.CASCADE)

The problem is that I get an error saying the form is invalid, and the fields uploader, book and language are required. How do I resolve this?

Upvotes: 0

Views: 4214

Answers (2)

Alasdair
Alasdair

Reputation: 309029

The initial data is used to display the defaults when the form is initially displayed. It isn't used when those values are missing from the submitted form data. If fields like book and uploader are set from the URL or logged-in user, then you should leave them out of the form completely, instead of setting them in the initial data. You can then set the values on the instance in the form_valid method before the form is saved.

from django.contrib.auth.mixins import LoginRequiredMixin

class PictureCreateView(LoginRequiredMixin, CreateView):
    model = Upload
    fields = ['other_field1', 'other_field2', ...]  # leave out book, language and uploader

    def form_valid(self, form):
        self.book_id = self.kwargs['book_id']
        book = Book.objects.get(id=self.book_id)
        form.instance.book = book
        form.instance.language = ????
        form.instance.uploader = self.request.user
        return super(

The LoginRequiredMixin makes sure that only logged-in users can access the view.

You may want to use get_object_or_404 to handle the case where book_id refers to a book that does not exist.

Upvotes: 6

talkingtoaj
talkingtoaj

Reputation: 888

One thought, initial doesn't fill the model for submission. You need to do that in init

def __init__(self):
    super(PictureCreateView, self).__init__()
    self.fields['book'] = self.initial['book']
    self.fields['uploader'] = self.initial['uploader']
    self.fields['language'] = self.initial['book']

Or, if you don't want to set the fields, make sure they are optional in your original model:

class Upload(models.Model):
    uploader = models.ForeignKey('uploader', on_delete=models.CASCADE, null=True, blank=True)
    book = models.ForeignKey('book', on_delete=models.CASCADE, null=True, blank=True)
    language = models.ForeignKey('language', on_delete=models.CASCADE, null=True, blank=True)

Upvotes: 2

Related Questions