I have started learning Django recently using a Udemy course. While going through the course instructor asked to save values from a Form to database.
After searching on the internet I figured out how to put form values into database and everything is working fine. Below is my views.py and forms.py files.
forms.py
class FormName(forms.Form):
fname = forms.CharField( label="First Name")
lname = forms.CharField(label="Last name:")
email = forms.EmailField()
verify_email = forms.EmailField(label='Verify Email:')
def clean(self):
all_clean_data = super().clean()
email = all_clean_data['email']
vmail = all_clean_data['verify_email']
if email != vmail:
raise forms.ValidationError("Check the emails")
views.py
def signup(request):
form = forms.FormName()
if request.method == 'POST':
form = forms.FormName(request.POST)
if form.is_valid():
post = User()
post.fname=request.POST.get('fname')
post.lname=request.POST.get('lname')
post.email=request.POST.get('email')
post.save()
return render(request,'third_app/greet.html')
else:
return render(request,'third_app/oops.html',{'form':form})
return render(request, 'third_app/signup.html',{'form':form})
Now coming to question, the instructor is using Meta class to store the form values to the database. Below are his forms.py and views.py files. I am curious about what the difference is between my method and the instructor's.
forms.py
class FormName(forms.ModelForm):
class Meta():
model = User
fields = 'all'
views.py
def signup(request):
form = forms.FormName()
if request.method == 'POST':
form = forms.FormName(request.POST)
if form.is_valid():
form.save(commit=True)
return render(request,'third_app/greet.html')
else:
return render(request,'third_app/oops.html',{'form':form})
return render(request, 'third_app/signup.html',{'form':form})
Thanks.
The Django docs explain this very well. It's what is known as a ModelForm:
If you’re building a database-driven app, chances are you’ll have forms that map closely to Django models. For instance, you might have a BlogComment model, and you want to create a form that lets people submit comments. In this case, it would be redundant to define the field types in your form, because you’ve already defined the fields in your model.
For this reason, Django provides a helper class that lets you create a Form class from a Django model.
So, to answer your question, your method uses a regular form (forms.Form) where you define the form fields, perform validation and then save each field individually in your view. When using form.ModelForm, field validation and saving is taken care of for you. Seeing as you have already defined what your fields are, the ModelForm uses this to perform the validation. The save() method conveniently saves each field to the database.
Related
I'm using a django form in conjunction with a wagtail form. The django form will record some fields that will be on any form of this type: name, email and the wagtail form will record extra data defined by the form page creator specific to that instance.
I've overloaded the serve method to capture both sets of data and I can process both forms, but I'm stuck when trying to add the logic to relate the form contents to each other so that when one submission set is deleted, the other set will be as well. I think what I need is a foreign key.
The following code fails at form_submission.event_submission = a.id where I'd like to take the id from the wagtail form submission and add that as a foreign key to the django form, so that when the wagtail form portion is deleted, the other is deleted as well, and so that I can have a usable link between the two form submissions.
def serve(self, request, *args, **kwargs):
if request.method == 'POST':
form = EventSignupForm(request.POST)
wagtail_form = self.get_form(request.POST, request.FILES, page=self, user=request.user)
if form.is_valid() and wagtail_form.is_valid():
a = self.process_form_submission(wagtail_form)
form_submission = form.save(commit=False)
form_submission.event_submission = a.id
form_submission.save()
return self.render_landing_page(request, form_submission, *args, **kwargs)
else:
form = EventSignupForm()
wagtail_form = self.get_form(page=self, user=request.user)
context = self.get_context(request)
context['form'] = form
context['wagtail_form'] = wagtail_form
return TemplateResponse(
request,
self.get_template(request),
context
)
The form submission class and django model form looks like this. I think the ForeignKey I have in the Model isn't right, but I don't know. Any help?
class EventFormSubmission(AbstractFormSubmission):
cancellation_id = models.CharField(max_length=7)
class EventSignup(models.Model):
"""
A model to contain signup info for an event: name, email.
"""
event_submission = models.ForeignKey(EventFormSubmission, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
email = models.EmailField()
I solved this by adding the extra fields I wanted (name, email) to the EventFormSubmission and then using a regular django form (not a ModelForm) to collect these pieces of information.
I have two models with a ManyToMany relationship:
class Topping(models.Model):
name = models.CharField(max_length=200)
class Pizza(models.Model):
name = models.CharField(max_length=200)
toppings = models.ManyToManyField(Topping, blank=True)
I have this form to update a pizza:
class PizzaUpdateForm(forms.ModelForm):
class Meta:
model = Pizza
fields=('name', 'toppings')
widgets={'toppings': Select2TagWidget}
The Select2TagWidget is imported from django_select2. I would like to use it to allow dynamic pizza topping creation (sounds cool, doesn't it?).
This form is used through this view:
def update_pizza(request, pk):
pizza = Pizza.objects.get(pk=pk)
if request.method == 'POST':
form = PizzaUpdateForm(request.POST, instance=pizza)
if form.is_valid():
form.save()
else:
form = PizzaUpdateForm(instance=pizza)
return render(request, 'pizza/pizza_form.html',
{'form': form, 'pizza': pizza})
Right now, it works for pre-registered toppings but I would like to allow my users to define new toppings via this same form, which is why I'm using select2 in the first place. Unfortunately, the form doesn't validate if I do so right now, which is to be expected.
If I try to add an illegal_topping, here's what happens:
"illegal_topping" is not a valid value.
I think that I need to iterate over form['toppings'].value() in the if request.method == 'POST' block before calling form.is_valid() in order to create the new toppings, using get_or_create or something similar.
However, if I do that, the values are a mix of existing toppings' IDs and new toppings' names. Everything is a string, so I'm not sure how to evaluate which one are new toppings and which ones were pre-existing.
Maybe would it be great if these values consist only of toppings' names, but I don't know how to achieve that.
Maybe my approach is completely wrong because I'm new to web development and Django in particular, so I would really love to have your input on this. It seems to me that this must be a relatively common problem… but I couldn't find (or understand) help. I know that there must be some M2M problems I'm not anticipating, so any guidance is more than welcome.
I guess you are allowing users to either select toppings that already exits or they can create new one, right ? (create_top is name of input filed in which user requests for new topping)
def update_pizza(request, pk):
pizza = Pizza.objects.get(pk=pk)
if request.method == 'POST':
form = PizzaUpdateForm(request.POST, instance=pizza)
if form.is_valid():
ctoppings = []
form = form.save(commit=False)
if not request.POST.get['create_top'] = '':
t, create = Topping.objects.get_or_create(name = request.POST.get['create_top'])
temp_top = request.POST.getlist('toppings')
for top in temp_top:
ctop = Topping.objects.get(name=top)
ctoppings.append(ctop)
ctoppings.append(t)
form.toppings.set(ctoppings)
form.save()
else:
form = PizzaUpdateForm(instance=pizza)
return render(request, 'pizza/pizza_form.html',
{'form': form, 'pizza': pizza})
see if this can help you.
I found a dirty way.
class PizzaUpdateForm(forms.ModelForm):
class Meta:
model = Pizza
fields=('name', 'toppings')
widgets={'toppings': Select2TagWidget}
toppings = forms.ModelMultipleChoiceField(queryset=Topping.objects.all(),
to_field_name='name',
widget=Select2TagWidget,
required=False)
def update_pizza(request, pk):
pizza = Pizza.objects.get(pk=pk)
if request.method == 'POST':
form = PizzaUpdateForm(request.POST, instance=pizza)
for topping_name in form['toppings'].value():
topping, created = Topping.objects.get_or_create(name=topping_name)
if created: topping.save()
if form.is_valid():
form.save()
else:
form = PizzaUpdateForm(instance=pizza)
return render(request, 'pizza/pizza_form.html',
{'form': form, 'pizza': pizza})
However, I feel like it would be bettre if the topping creation logic wasn't in the view but in the pizza.forms module. That'll do for now I guess.
I am newbie to Django. I have some troubles with forms after moving into new verison. Following,
1, The model
class UserProfileDetails(models.Model):
user = models.OneToOneField(User)
profilePicture = models.ImageField('Profile Picture',upload_to='static/ProfilePic/', null=True,blank=True)
def __str__(self):
return self.user.username
2, The form
class imageUploadForm(forms.ModelForm):
class Meta:
model= UserProfileDetails
fields = ['user','profilePicture']
3, And finally the view function
def upload_pic(request):
current_user = request.user
if request.method == 'POST':
form = imageUploadForm(request.POST, request.FILES)
if form.is_valid():
pic = form.cleaned_data['profilePicture']
m = UserProfileDetails(user= current_user.id,profilePicture=pic)
m.save()
else:
raise NotImplemented("What if the user doesn't have an associated profile?")
return HttpResponseRedirect(reverse('polls:profile'))
This code worked with Django 1.8. But after porting to Django 1.10.4, the form is getting invalid. I believe, the problem is with OneToOneField.
IMP: Also, i am using pinax account app for account management.
Why this form is getting invalid?
When you submit the form, it doesn't seem as though a correct input has been given for both fields (user and profile picture). My guess is that you aren't sending through the user in the form which means it is invalid. So you are only uploading the image.
You do not need to have 'user' in the form fields attribute as you already access that in the view with 'request.user'. So remove the 'user' field from the form.
Also, to make sure it is correct, change 'user=current_user.id' to 'user=current_user' so you are match instance with instance rather than instance with id.
I run into this pattern all the time and I'm wondering if there's a better way to handle it. Lots of my models contemplate the idea of 'creator' - in other words, I want to make sure the user who created the object is saved as the creator. As such, my models almost always include
creator = models.ForeignKey(User)
There doesn't seem to be a way of defaulting this user to the user who created it (request.user). As such I find myself building model forms and adding creator as a HiddenInput()
class MyModelForm(ModelForm):
class Meta:
model = MyModelForm
fields = ['name', 'creator']
widgets = {
'creator': HiddenInput()
}
and then binding the form in the view with initial
form = MyModelForm(initial={'creator': request.user})
and checking on POST to make sure no one dickered with the form (full view):
def create(request):
form = MyModelForm(initial={'creator': request.user})
if request.method == 'POST':
if int(request.POST['creator']) != int(request.user.id):
return render(request,'500.html', {'message':'form tampering detected'})
form = FeedGroupForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('index'))
return render(request, 'mymodelformpage.html',{'form':form})
I'm fine with this but it seems like an anti-pattern. It strikes me that there ought to be a way to default the creator in the model.
creator = models.ForeignKey(User, default=request.user)
Or do it in a post_save perhaps?
No, this is not the right way. The correct way is to exclude the creator field from the form, then set it on save:
if form.is_valid:
obj = form.save(commit=False)
obj.creator = request.user
obj.save()
... redirect ...
I'm trying to make a form for updating user accounts in Django that is autopopulated with the current account data for that particular user. I can do this with HTML, but would rather find a way to make it work using Django's form system. I can't use Django's built-in user management due to certain constraints and am having to construct my own. I'm lost on where to start with this.
If you're using a normal form (sublassing forms.Form), pass data in using the initial keyword argument to your form constructor:
# forms.py
class MyUserForm(forms.Form):
username = forms.CharField(...
first_name = forms.CharField(...
...
# views.py
if request.method == 'POST':
# Process form
form = MyUserForm(request.POST)
...
else:
form = MyUserForm(initial={
'username': request.user.username,
'first_name': request.user.first_name,
...
}
...
An even easier way, though, is using Django's ModelForms. Something like:
# forms.py
class MyUserForm(forms.ModelForm):
class Meta:
model = User
# views.py
if request.method == 'POST':
form = MyUserForm(request.POST, instance=request.user)
...
else:
form = MyUserForm(instance=request.user)
...
Read the forms chapter of The Django Book online, for free. Then look into initial_data passed to forms. That should get you rolling.