I want to pass a query string e.g., ?refcode='A1234' to a hidden field called inbound_referral_code in a ModelForm.
My model is as follows:
class User(models.Model):
email = models.EmailField(max_length=255, blank=False, unique=True)
inbound_referral_code = models.CharField(max_length=255)
My ModelForm is currently as follows:
class UserForm(forms.ModelForm):
model = User
fields = ['email', 'inbound_referral_code']
widgets = {'inbound_referral_code': forms.HiddenInput()}
My View is:
def register(request):
if request.method == 'POST':
form = UserForm(request.POST)
[...]
else:
form = UserForm()
return render(request, 'register.html', {'form': form})
And my template is currently:
<form action="{% url 'register' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit"/>
</form>
Two questions:
How do I assign ?refcode parameter to inbound_referral_code field?
What happens if ?refcode isn't provided?
Combining the different answers, the following solution worked:
Set the "initial" value of the form parameter, and ensure the template renders with the bound form if validation fails. The correct view function is:
def register(request):
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
return redirect([...])
else:
refcode = request.GET.get('refcode')
form = UserForm(intial={'inbound_referral_code': refcode)
return render(request, 'register.html', {'form': form})
Note that the bottom return render(...) needed to be moved so that it is also called with the form from the POST request if it contains validation errors...
To assign the refcode, you need to pass it into the form, so you pass in something other than request.POST that contains it, change it before
dict = request.POST.copy()
dict["inbound_referral_code"] = request.POST.get("refcode")
form = UserForm(dict)
# ...
or after validating:
if form.is_valid():
form.cleaned_data["inbound_referral_code"] = request.POST.get("refcode")
If it isn't provided, you can check for that and pass a custom value, or set a default when defining the form/model.
To set it in the template, you can pass an initial value
else:
form = UserForm(initial={"inbound_referral_code": "ref-value-here"})
return render(request, 'register.html', {'form': form})
Related
Updated
i have two tables(models) one for product name and features and the other for prices
i normalized to database tables
like this
class Model(models.Model):
name = models.CharField(max_length=23)
def __str__(self):
return self.name
class Feature(models.Model):
model = models.ForeignKey(Model,on_delete=models.CASCADE)
price = models.IntegerField()
company = models.CharField(max_length=33)
def __str__(self):
return self.model
my views.py
def create(request):
createmodel = ModelForms(prefix='createmodel')
createfeature = FeatureForm(prefix='createfeature')
if request.method == 'POST':
createmodel = ModelForms(request.POST,prefix='createmodel')
if createmodel.is_valid():
createmodel.save()
name = createmodel.cleaned_data['name']
if createfeature.is_valid():
createfeature = FeatureForm(request.POST,prefix='createfeature',model=name)
createfeature.save()
return redirect('/lists/')
return render(request,'forms/create.html',{'createmodel':createmodel,'createfeature':createfeature})
my forms.py
class ModelForms(forms.ModelForm):
class Meta:
model = Model
fields = [
'name'
]
class FeatureForm(forms.ModelForm):
read_only = ['model']
class Meta:
model = Feature
fields = [
'price','company'
]
but the form wont saved
how to submit both forms at the same time with providing the exact foreign key (as filled in ModelForm to FeatureForm)in one go
i know how to do it in updating two forms , but how to create submit two forms with providing the instance directly
much respect
Doing it is simple, since you did not provide views.py and .html I had to give you my example.
views.py
def (request):
if request.method == 'POST':
# PROCESS POST METHOD FORM
user_form = UserEditForm(instance=request.user, data=request.POST)
profile_form = ProfileEditForm(instance=request.user.profile_user, data=request.POST, files=request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
else:
# HERE WE ADD TWO FORMS IN CONTEXT
user_form = UserEditForm(instance=request.user)
profile_form = ProfileEditForm(instance=request.user.profile_user)
return render(request, 'accounts/edit.html', {'user_form': user_form, 'profile_form': profile_form})
edit.html
<!-- Above Code -->
<form action="." method="post" enctype="multipart/form-data">
{{ user_form.as_p }}
{{ profile_form.as_p }}
{% csrf_token %}
<p><input type="submit" value="Save changes"></p>
</form>
<!-- Below Code -->
You can make two separate forms for two models.
Its better to use form prefix in such cases, in my case it will become:
def (request):
if request.method == 'POST':
# PROCESS POST METHOD FORM
user_form = UserEditForm(request.POST, prefix="user_form")
profile_form = ProfileEditForm(request.POST, prefix="profile_form", files=request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
else:
# HERE WE ADD TWO FORMS IN CONTEXT
user_form = UserEditForm(prefix="user_form")
profile_form = ProfileEditForm(prefix="profile_form")
return render(request, 'accounts/edit.html', {'user_form': user_form, 'profile_form': profile_form})
I'm trying to populate my ModelForm with some of data that I have submitted to previous HTML page which is also ModelForm.
I just want to pass it to another form so it doesn't have to be written twice.
I've tried couple solutions from stackoverflow but they are 6+ years old, kinda outdated and also couldnt come up with solution from django docs https://docs.djangoproject.com/en/2.2/topics/forms/
I have two models, which have same fields which are name and boxid
I need to pass it from first input to second(to populate it).
forms.py
class NewCashierForm(forms.ModelForm):
class Meta:
model = Cashier
fields = ("cashier_company", "cashier_dealer", "cashier_name", "cashier_boxid", "cashier_type", "cashier_package", "cashier_otheritem", "cashier_otheritemserial", "cashier_length", "cashier_promotion", "cashier_amount", "cashier_paymenttype")
labels = {"cashier_company":('Firma'), "cashier_dealer": ('Diler'), "cashier_name":('Ime i prezime'), "cashier_boxid":('Box ID'), "cashier_type":('Tip'), "cashier_package":('Paket'), "cashier_otheritem":('Drugi uredjaj'), "cashier_otheritemserial":('SBU'), "cashier_length":('Dužina'), "cashier_promotion":('Promocija'), "cashier_amount":('Iznos'), "cashier_paymenttype":('Nacin uplate')}
exclude = ['cashier_published']
def save(self, commit=True):
cashier = super(NewCashierForm, self).save(commit=False)
if commit:
cashier.save()
return cashier
class NewPersonForm(forms.ModelForm):
class Meta:
model = Person
fields = {"person_name", "person_adress", "person_phone", "person_boxid"}
labels = {"person_name":('Ime i prezime'), "person_adress":('Adresa'), "person_phone":('Telefon'), "person_boxid":('Box ID')}
def save(self, commit=True):
person = super(NewPersonForm, self).save(commit=False)
if commit:
person.save()
return person
views.py
def addcashier(request):
if request.method == 'GET':
form = NewCashierForm()
else:
form = NewCashierForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
return redirect('/byauthor')
return render (request, 'main/addcashier.html', {'form':form})
def addperson(request):
if request.method == 'GET':
form = NewPersonForm()
else:
form = NewPersonForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
return redirect('/addcashier')
return render (request, 'main/addperson.html', {'form':form})
addperson.html and addcashier.html
{% extends "main/base.html" %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button class="btn" type="submit">Unos i dodavanje pretplate</button>
</form>
<input type="button" value="Otkazi unos" onclick="window.history.back()" />
{% endblock %}
Any help and/or hint is appreciated.
To prepopulate the form, you need to pass an argument initial={} when initializing your form for the GET call. Since you are passing data from one view to another, you should use sessions.
def addperson(request):
if request.method == 'GET':
form = NewPersonForm()
else:
form = NewPersonForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
request.session["person_form"] = request.POST.dict() #save the form as a dict in request.sessions
return redirect('/addcashier')
return render (request, 'main/addperson.html', {'form':form})
Then in your second view, use this data from sessions to initialize the form.
def addcashier(request):
if request.method == 'GET':
# get the form data from the request.session
form_data = request.session.pop('person_form', {})
box_id = form_data.get("person_boxid")
name = form_data.get("person_name")
form = NewCashierForm(initial={"cashier_name":name, "cashier_boxid":box_id}) # initialize the form with the data
else:
form = NewCashierForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
return redirect('/byauthor')
return render (request, 'main/addcashier.html', {'form':form})
I have a form that involves uploading a profile picture. I have it working so that I can upload images in the /admin/ interface and display them correctly, but I cannot get my Modelform to save the image.
Here is what I have:
models.py
class Candidate(models.Model):
UserID = models.ForeignKey(User, on_delete=models.CASCADE)
ElectionID = models.ForeignKey(Election, on_delete=models.CASCADE)
Bio = models.CharField(max_length=500, blank=True)
ProfilePicture = models.ImageField(upload_to="profilepics/", null=True, blank=True)
forms.py
class AddCandidateForm(forms.ModelForm):
class Meta:
model = Candidate
fields = ['ElectionID', 'Bio', 'ProfilePicture']
cand_reg.html (Template)
{% block content %}
<h1>Register as a candidate</h1>
<form method="POST" class="post-form">
{% csrf_token %}
<h2>Select an election:</h2><br>
{{form.ElectionID}}<br>
<h2>Enter your bio:</h2><br>
{{form.Bio}}<br>
<h2>Upload a profile picture:</h2><br>
{{form.ProfilePicture}}<br>
<button type="submit">Register</button>
</form>
{% endblock %}
When I try the view function like so I get the error:
MultiValueDictKeyError at /register/
"'ProfilePicture'"
views.py
def add_candidate(request):
if request.method == 'POST':
form = AddCandidateForm(request.POST, request.FILES)
if form.is_valid():
candidate = form.save(commit=False)
candidate = request.FILES['ProfilePicture']
candidate.UserID = request.user
candidate.save()
return redirect('/home/')
else:
form = AddCandidateForm()
return render(request, 'cand_reg.html', {
"form": form
})
views.py
When I remove the offending line, the error goes away.
def add_candidate(request):
if request.method == 'POST':
form = AddCandidateForm(request.POST, request.FILES)
if form.is_valid():
candidate = form.save(commit=False)
# candidate = request.FILES['ProfilePicture']
candidate.UserID = request.user
candidate.save()
return redirect('/home/')
else:
form = AddCandidateForm()
return render(request, 'cand_reg.html', {
"form": form
})
However, this doesn't actually save the image, so when I try to render it in a separate template, I get an error then.
Can anyone help me understand why the image isn't uploading?
Thanks in advance :)
You must set the ProfilePicture attribute of the model and not the instance itself (candidate = request.FILES['ProfilePicture']).
Change to:
candidate = form.save(commit=False)
candidate.ProfilePicture = request.FILES['ProfilePicture']
candidate.UserID = request.user
candidate.save()
Change your HTML form to accept files as well. Change to: <form method="POST" enctype="multipart/form-data" class="post-form">. When a form includes file inputs (<input type="file" />), then it must be encoded differently than it used when it includes only text. More here. If you right-click and inspect the {{form.ProfilePicture}} you'll see that this is actually a file input.
Extra one:
Please, do not name your class attributes (ProfilePicture, UserID etc) in PascalCase. Use snake_case instead (profile_picture, user_id etc).
I am trying to create a frontend form in my Django site that will allow users to add entries to my SQL database.
But when I use the form nothing happens in my database. What am I doing wrong?
I thought the right way would be to use the ModelForm technique.
My models looks like this:
class Actor(models.Model):
name = models.CharField(max_length=200)
wage = models.IntegerField(default=3100)
def __str__(self):
return self.name
So I wrote this in my forms.py:
from django import forms
from .models import Actor
class ActorForm(forms.ModelForm):
class Meta:
model = Actor
fields = ['name', 'wage']
form = ActorForm()
I then added this to my views.py:
def get_actor(request):
if request.method == 'POST':
form = ActorForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('/scenes/thanks/')
else:
form = ActorForm()
return render(request, 'scenes/actor.html', {'form': form})
def thanks(request):
return render(request, 'scenes/thanks.html',)
And this in a template called actors.html
<form action="/scenes/actor/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
You have to call the model form's save() method after checking that it's valid:
def get_actor(request):
if request.method == 'POST':
form = ActorForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/scenes/thanks/')
else:
form = ActorForm()
return render(request, 'scenes/actor.html', {'form': form})
Starting to beat my head against the wall...perhaps I am missing something simple.
models.py
class GFImage(models.Model):
image = models.ImageField(upload_to = 'uploads', null=True, blank=True)
views.py
def addImage(request):
errors = []
if request.method == 'POST':
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
form.save()
urlRedirect = "/home"
return redirect(urlRedirect)
else:
form = ImageForm()
return render(request, "/add_image.html", {'form': form})
forms.py
class ImageForm(ModelForm):
class Meta:
model = GFImage
add_image.html
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
Whatever I do, my form will not use the ClearableFileInput widget. It should default automatically, but even assigning it in the form's META will not work. What else could be blocking Django from using the clearable widget?
The ClearableFileInput will only display the clear checkbox when there's an initial file selected. Looking at your form, it looks like a a new form without initial data, so the checkbox won't be displayed.
def render(self, name, value, attrs=None):
.. snip ..
if value and hasattr(value, "url"):
template = self.template_with_initial
substitutions['initial'] = format_html(self.url_markup_template,
https://github.com/django/django/blob/5fda9c9810dfdf36b557e10d0d76775a72b0e0c6/django/forms/widgets.py#L372