Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django : Can I Pre-fill forms with a model instance datas in form.py?

i've seen a way to do what I want in the views.py file, but as I'm probably going to use the form in several different places, and because I believe it belongs more in forms.py, I was wondering if and how I could pre-fill my form with the datas already stored in the database for one particular user without modifying the views. Seeing what I tried will speak for itself :

models.py :

class Customers(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='details')
    city = models.CharField(max_length=50, blank=True, null=True)
    dpt = models.IntegerField(blank=True, null=True)
    adress = models.CharField(max_length=100, blank=True, null=True)
    cplt_add = models.CharField(max_length=100, blank=True, null=True)
    phone = models.CharField(max_length=14, blank=True, null=True)

forms.py :

class CustomerForm(ModelForm):

    def __init__(self):

        for field in self.fields:

            user_infos = Customers.objects.get(pk=request.user.details.id)
            initial_value = user_infos.field
            field(initial=initial_value)


    def clean_form(self):

        ...


    class Meta:

        model = Customers
        fields = ['city', 'adress', 'dpt', 'cplt_add', 'phone']
        widgets = {
            'city': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Ville'}),
            'dpt': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Département'}),
            'adress': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Adresse'}),
            'cplt_add': TextInput(attrs={
                'class': 'form_input',
                'placeholder': "Complément d'adresse"}),
            'phone': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Téléphone'}),
            }

This code, sadly but without big surprise, returns :

'CustomerForm' object has no attribute 'fields'

Any advice on how to do this with good practice methodology ?

like image 298
AlexMdg Avatar asked Sep 09 '18 14:09

AlexMdg


1 Answers

You tackle the problem at the wrong level: Django already has support for this, and writing it in the __init__ yourself is not a good idea (except for some specific edge- and corner cases).

You can thus remove the __init__ override from the ModelForm:

# app/forms.py

class CustomerForm(ModelForm):

    # remove the __init__ override

    class Meta:
        model = Customers
        fields = ['city', 'adress', 'dpt', 'cplt_add', 'phone']
        widgets = {
            'city': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Ville'}),
            'dpt': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Département'}),
            'adress': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Adresse'}),
            'cplt_add': TextInput(attrs={
                'class': 'form_input',
                'placeholder': "Complément d'adresse"}),
            'phone': TextInput(attrs={
                'class': 'form_input',
                'placeholder': 'Téléphone'}),
            }

In the view (or somewhere else where you want to construct the form), we can then pass the instance through the instance= parameter of the constructor:

# app/views.py

def my_view(request):
    customer=Customers.objects.get(pk=request.user.details.id)
    my_form = CustomerForm(instance=customer)
    # ...
    # return HttpResponse object

Django will then ensure that the values are filled in the correct forms (as if it were the initial values). If you provide both initial values and an instance, the initials take precendence as described in the documentation on the ModelForm:

As with regular forms, it's possible to specify initial data for forms by specifying an initial parameter when instantiating the form. Initial values provided this way will override both initial values from the form field and values from an attached model instance.

like image 55
Willem Van Onsem Avatar answered Sep 27 '22 23:09

Willem Van Onsem