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 ?
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 initial
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With