Django: Display user's previous choices for a ModelForm in the template

2412 views django
2

I am trying to create a user profile page where users can see and update their preferences for certain things, like whether they are vegetarian, or have a particular allergy, etc. I want the data to be displayed as a form, with their current preferences already populating the form fields.

So I've created the following Model:

class FoodPreferences(models.Model):

    user = models.OneToOneField(User, on_delete=models.CASCADE)  # One user has one set of food prefs
    vegetarian = models.BooleanField()
    vegan = models.BooleanField()
    ...

that's referenced in my forms.py:

class FoodPreferencesForm(forms.ModelForm):

    class Meta:
        model = FoodPreferences
        exclude = ('user', )

I've tried creating a view that inherits FormView and then referencing the form, like this:

class UserProfileView(generic.FormView):

    template_name = "registration/profile.html"
    form_class = FoodPreferencesForm
    success_url = reverse_lazy('user_profile')

This saves the form to a instance of the model correctly, but obviously it just displays the blank form again, after updating, so the user has no idea what their current preferences are.

To implement this I thought I might need to override get() and post() to get the instance of FoodPreferences for the user, and then pass those values into the form like you would a request.POST object. However, firstly, I don't know how to do that, and secondly I'd be taking responsibility for correctly updating the database, which the FormView was already doing.

This is what I've got for that solution:

def get(self, request, *args, **kwargs):

    prefs = FoodPreferences.objects.get(user=request.user)
    form = self.form_class(prefs)
    return render(request, self.template_name, {'form': form, })


def post(self, request, *args, **kwargs):

    form = self.form_class(request.POST)
    if not form.is_valid():
        return render(request, self.template_name, {'form': form, 'error': 'Something went wrong.'})

    curr_prefs = FoodPreferences.objects.update_or_create(form.fields)
    prefs.save()

    return render(request, self.template_name, {'form': form, })

but I get a TypeError: argument of type 'FoodPreferences' is not iterable on the line in get():

form = self.form_class(prefs)

because it's not expecting a model instance.

Am I thinking about this in the right way? This seems like a common enough problem that Django would have something inbuilt to do it, but I can't find anything.

answered question

1 Answer

2

You should only rarely need to define get or post in a class-based view, and you definitely don't here.

To start with, you need to use a more appropriate base class for your view. Here you want to update an existing item, so you should use UpdateView.

Secondly, you need to tell the class how to get the existing object to update, which you can do by definining get_object. So:

class UserProfileView(generic.UpdateView):

    template_name = "registration/profile.html"
    form_class = FoodPreferencesForm
    success_url = reverse_lazy('user_profile')

    def get_object(self, queryset=None):
        return self.request.user.foodpreferences
        # or, if you aren't certain that the object already exists:
        obj, _ = FoodPreferences.objects.get_or_create(user=self.request.user)
        return obj

posted this

Have an answer?

JD

Please login first before posting an answer.