26

I'm having a problem with logged users and a Django ModelForm. I have a class named _Animal_ that has a ForeignKey to User and some data related to the animal like age, race, and so on.

A user can add Animals to the db and I have to track the author of each animal, so I need to add the request.user that is logged when the user creates an animal instance.

models.py

class Animal(models.Model):
    name = models.CharField(max_length=300)
    age = models.PositiveSmallIntegerField()
    race = models.ForeignKey(Race)
    ...
    publisher = models.ForeignKey(User)
    def __unicode__(self):
        return self.name

class AnimalForm(ModelForm):
    class Meta:
        model = Animal

The main goal is hide the publisher field in the form, and submit the logged user when hitting save button.

I can catch the current user in the view using initial, but what I also want is not display the field.

views.py

@login_required
def new_animal(request):
    if request.method == "POST":
        form = AnimalForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('/')
        else:
            variables = RequestContext(request, {'form': form})
            return render_to_response('web/animal_form.html', variables)
    else:
        form = AnimalForm(initial={'publisher': request.user})
    variables = RequestContext(request, {'form': form})
    return render_to_response('web/animal_form.html', variables)
zaidfazil
  • 9,017
  • 2
  • 24
  • 47
amb
  • 4,798
  • 6
  • 41
  • 68

4 Answers4

46

You just need to exclude it from the form, then set it in the view.

class AnimalForm(ModelForm):
    class Meta:
        model = Animal
        exclude = ('publisher',)

... and in the view:

    form = AnimalForm(request.POST)
    if form.is_valid():
        animal = form.save(commit=False)
        animal.publisher = request.user
        animal.save()

(Note also that the first else clause - the lines immediately following the redirect - is unnecessary. If you leave it out, execution will fall through to the two lines at the end of the view, which are identical.)

Jamie Counsell
  • 7,730
  • 6
  • 46
  • 81
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • can you please explain what is this line of code doing: `animal = form.save(commit=False)`? – yogeshiitm Jan 15 '21 at 12:33
  • Read this: https://stackoverflow.com/questions/12848605/django-modelform-what-is-savecommit-false-used-for, yogeshiitm – Chris Aug 01 '22 at 02:29
9

Another way (slightly shorter):
You need to exclude the field as well:

class AnimalForm(ModelForm):
    class Meta:
        model = Animal
        exclude = ('publisher',)

then in the view:

animal = Animal(publisher=request.user)  
form = AnimalForm(request.POST, instance=animal)
if form.is_valid():
     animal.save()
matino
  • 17,199
  • 8
  • 49
  • 58
  • This is a better answer. The `form.is_valid` here correctly calls the `models .clean()` and has access to the `publisher` field. In the other answer, the `publisher` field at that check would not yet be available, making it impossible to use DB `clean()` validation, and forcing the validation at the form level. – mateuszb Jun 09 '21 at 19:24
4

I would add it directly to the form:

class AnimalForm(ModelForm):
    class Meta:
        model = Animal
        exclude = ('publisher',)

    def save(self, commit=True):
        self.instance.publisher = self.request.user
        return super().save(commit=commit)

This is in my opinion the cleanest version and you may use the form in different views.

melbic
  • 11,988
  • 5
  • 33
  • 37
0

If you are using ModelAdmin you should add method get form on your ModelAdmin

class BlogPostAdmin(admin.ModelAdmin):
    form = BlogPostForm

    def get_form(self, request, **kwargs):
        form = super(BlogPostAdmin, self).get_form(request, **kwargs)
        form.request = request
        return from

and you can now access request in your ModelForm

class ProductAdminForm(forms.ModelForm):
    
    def save(self, commit: bool,  *args, **kwargs):
        self.instance.user = self.request.user
        return super().save(commit=commit)
        pass
Mark Anthony Libres
  • 906
  • 1
  • 7
  • 14