Setting an initial value in a Django Form - python

I have to setup an initial value in a form and somehow is not working, it is extremely strange as I have exactly the same code in another view, but in this case my approach is not working:
views.py
#login_required
def add_lead(request):
if request.method == 'POST':
lead_form = LeadsForm(request.POST)
if lead_form.is_valid():
lead_form.save()
messages.success(request, 'You have successfully added a new lead')
return HttpResponseRedirect(reverse('add_lead'))
else:
messages.error(request, 'Error updating your Form')
else:
user = {"agent":request.user}
lead_form = LeadsForm(request.POST or None, initial = user)
return render(request,
'account/add_lead.html',
{'lead_form': lead_form}
)
forms.py
class LeadsForm(forms.ModelForm):
class Meta:
model = Leads
fields = ('project_id','company','agent','point_of_contact','services','expected_licenses',
'expected_revenue','country', 'status', 'estimated_closing_date'
)
widgets = {'estimated_closing_date': DateInput(),
}
Essentially, the agent is the logged user, so I'm passing request.user as a variable, but I have not succeeded, which is very strange because I have that same logic in another form
Any help will be appreciated

If you want to make a form with a foreign key you can use ModelChoiceField. In your case you can use:
class LeadsForm(forms.ModelForm):
agent = forms.ModelChoiceField(queryset=User.objects.all())
class Meta:
model = Leads
fields = ('project_id','company','agent','point_of_contact','services','expected_licenses',
'expected_revenue','country', 'status', 'estimated_closing_date'
)
widgets = {'estimated_closing_date': DateInput(),
}
Then you can assign data with user_id in your form initial.

Related

Manually set model fields in ModelForm

I have a model with a foreign key and a unique constraint as follows:
class Menu(models.Model):
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
name = models.CharField(max_length=128)
date_menu = models.DateField()
class Meta:
constraints = [
models.UniqueConstraint(fields=['tournament', 'name', 'date_menu'], name="unique_name_menu")
]
I would like to create a form to add instance of Menu. However the value of tournament is set by the URL of the page. I do not want the user to be able to set it.
For this I use a modelForm, excluding the tournament field :
class MenuForm(forms.ModelForm):
date_menu = forms.DateField(initial=datetime.datetime.now())
class Meta:
model = Menu
exclude = ['tournament']
Here is my view :
def add_menu(request, tournament_slug):
tournament = get_object_or_404(Tournament, slug=tournament_slug)
form = MenuForm(request.POST or None)
if form.is_valid():
menu_id = form.save(commit=False)
menu_id.tournament = Tournament.objects.get(pk=1)
menu_id.save() # I get the integrity error only here
return HttpResponseRedirect(reverse('admin'))
return render(request, "view.html", {'form': form, 'formset': formset, "tournament": tournament})
My problem is that when I call the .is_valid() function on this form the uniqueness condition cannot be checked as the tournament field is not set. As a result I get an integrity error when calling the save function in the view.
The question is : how can link the Menu instance created by the form to add the tournament field before checking if it's valid? If it's not the right way of doing it, how can I check the uniqueness of the model instance and return the corresponding errors to the template when needed?
I tried including the tournament field as hidden field in the view, it works but I don't know if that's the best way of doing it...
You should simply instantiate the form with an unsaved instance of Menu so your view should be like:
def add_menu(request, tournament_slug):
tournament = get_object_or_404(Tournament, slug=tournament_slug)
if request.method == 'POST':
form = MenuForm(request.POST, instance=Menu(tournament=tournament))
if form.is_valid():
menu_id = form.save()
return HttpResponseRedirect(reverse('admin'))
else:
form = MenuForm(instance=Menu(tournament=tournament))
return render(request, "view.html", {'form': form, "tournament": tournament})
Also the form calls _get_validation_exclusions() and excludes fields not present in the form from validation. You can try to override validate_unique to overcome this:
class MenuForm(forms.ModelForm):
date_menu = forms.DateField(initial=datetime.datetime.now())
class Meta:
model = Menu
exclude = ['tournament']
def validate_unique(self):
exclude = self._get_validation_exclusions()
if 'tournament' in exclude:
exclude.remove('tournament') # Make sure `tournament` gets validated
try:
self.instance.validate_unique(exclude=exclude)
except ValidationError as e:
self._update_errors(e)
Note: I changed your view structure to avoid using MenuForm(request.POST or None) which is an antipattern. (Forms
can be valid even if nothing is sent in the POST data, with the way
you write such forms would be considered invalid).
Edit: As discussed in the comments perhaps the option of a hidden and disabled field is much better than overriding the forms validate_unique method:
class MenuForm(forms.ModelForm):
tournament = forms.ModelChoiceField(
queryset=Tournament.objects.all(),
widget=forms.HiddenInput(),
disabled=True
)
date_menu = forms.DateField(initial=datetime.datetime.now())
class Meta:
model = Menu
fields = ['tournament', 'name', 'date_menu']

Django ModelForm: Defining a value not passed into the template

I have a ModelForm, and I want to only pass some of the fields into the template. I would like to save one particular field to define after the POST request has been sent. Here is the ModelForm:
class CreateListingForm(ModelForm):
class Meta:
model = models.ListingModel
fields = ['name', 'image', 'description', 'price', 'category']
widgets = {
'description': Textarea()
}
And here is the Model:
class ListingModel(models.Model):
name = models.CharField(max_length=30)
image = models.ImageField(upload_to='images')
description = models.CharField(max_length=1000)
price = models.PositiveIntegerField()
category = models.CharField(max_length=15)
objects = models.Manager()
owner = models.CharField(max_length=100)
In the next code block, I am attempting to define the owner field according to the current user logged in (request.user.username):
#login_required(redirect_field_name=login_view)
def create_listing(request):
if request.method == "GET":
return render(request, "auctions/createlisting.html", {
"CreateListingForm": forms.CreateListingForm()
})
elif request.method == "POST":
form = forms.CreateListingForm(request.POST, request.FILES)
if form.is_valid():
try:
form.owner = request.user.username
print(form.owner)
form.save(commit=True)
except Exception:
return HttpResponseRedirect(reverse("create_listing_error"))
return HttpResponseRedirect(reverse("index")) #TODO
Now, when I say print(form.owner), the result is correct. However when I save the ModelForm, the owner field is left blank. Am I not defining the value of the owner field correctly?
You should not confuse the ModelForm with the instance it is wrapping. The fact that it prints something for form.owner is not that strange, you first set an attribute named .owner, an attribute that did not exists before. You should set the .owner of the .instance of the form:
#login_required(redirect_field_name=login_view)
def create_listing(request):
if request.method == 'POST':
form = forms.CreateListingForm(request.POST, request.FILES)
if form.is_valid():
form.instance.owner = request.user.username
form.save()
return redirect('name-of-some-view')
else:
form = forms.CreateListingForm()
return render(request, 'auctions/createlisting.html', {
'CreateListingForm': form
})
Where 'name-of-some-view' should be replaced by the name of some view to which you redirect in case the form was valid.
You should however consider changing the CharField of owner to a ForeignKey [Django-doc]. Imagine that later the user changes their username, then your ListingModels do no longer refer to a real user.

Django - Trying to a field that is not in the form but is in the model

Right now I have a model.form with one field nothing more, but the model has 3 fields(see reference down below) and 1 of them set by the form, one has a default to false as it should be and the last one I will set in the view but it won't correctly do it and idk why.
Model & Form.
class TeamMembership(models.Model):
user = models.ForeignKey(User)
team = models.ForeignKey(Team)
leader = models.BooleanField(default=False)
class TeamSettings_acceptForm(forms.ModelForm):
class Meta:
model = TeamMembership
fields = ('user',)
View
#login_required
def teamsettings_accept_applications(request, team_pk):
if request.method == 'POST':
logged_in_user = get_object_or_404(User, pk=request.user.pk)
requested_team = get_object_or_404(Team, pk=team_pk)
for member in requested_team.teammembership_set.all().order_by('-leader'):
if member.user.pk == request.user.pk and member.leader:
formaccept = TeamSettings_acceptForm(request.POST)
accepteduserid = formaccept.data['user']
teamapplications = TeamApplication.objects.all().filter(from_user=accepteduserid).count()
if teamapplications > 1:
messages.success(request, "Error")
return redirect('teamsettings_applications', team_pk=team_pk)
else:
if formaccept.is_valid():
teamapplications = TeamApplication.objects.all().filter(from_user=accepteduserid)
teamapplications.update(accepted=True)
formaccept.team = requested_team.pk
formaccept.save()
messages.success(request, "User has now been added to your team!")
return redirect('teamsettings_applications', team_pk=team_pk)
it should create a new row with that data and update the others.
All I get in return from Django is
staff_teammembership.team_id may not be NULL
You haven't processed the Model form into an instance of a model yet. So formaccept doesn't know what team is here, because it's an instance of TeamSettings_acceptForm which doesn't have team as a field. To fix this change the is_valid code:
if formaccept.is_valid():
teamapplications = TeamApplication.objects.all().filter(from_user=accepteduserid)
teamapplications.update(accepted=True)
# New code here
new_team_membership = formaccept.save(commit=false)
new_team_membership.team = requested_team.pk
new_team_membership.save()
messages.success(request, "User has now been added to your team!")
return redirect('teamsettings_applications', team_pk=team_pk)
Using commit=false is really handy with Modelforms.

Post method in Django models

I am new in Django forms and I need to insert/update some data into my database.
I have some model and in the django admin panel I introduce manually the user's phone and the IMEI number.
After that I create a form, a template.html and a view.
The form is as follows:
from django import forms
class Send2phone(forms.Form):
NumberOfCalls = forms.CharField(
min_length=1,
widget=forms.TextInput({'class': 'form-control'})
)
TimeBetweenCalls = forms.CharField(
widget=forms.TextInput({'class': 'form-control'})
)
PSAP = forms.CharField(
min_length=1,
widget=forms.TextInput({'class': 'form-control'})
)
And my view is:
def phone_config(request):
if request.method == 'POST':
form = Send2phone(request.POST)
if form.is_valid():
cleaned_data = form.cleaned_data
NumberOfCalls = cleaned_data.get('NumberOfCalls')
TimeBetweenCalls = cleaned_data.get('TimeBetweenCalls')
PSAP = cleaned_data.get('PSAP')
phone_model = Phone()
phone_model.id = 1
phone_model.user = donothing
phone_model.imei = donothing
phone_model.num_calls = NumberOfCalls
phone_model.time_btwn_calls = TimeBetweenCalls
phone_model.psap = PSAP
phone_model.save()
return redirect(reverse('gracias'))
else:
form = Send2phone()
return render(request, 'heroconfigurer/heroconfigurer.html', {'form': form})
def gracias_view(request):
return render(request, 'heroconfigurer/gracias.html')
My problem comes now when I create the view. First of all, I check if the method is post and I get the data from the form.
Then I check if the form is valid and I create the object Phone. After that assign the different parameters and save them.
Inserting the data from the form is working good but imei and user ara being deleted if I don't specify them.
How can I insert data in the database models where exist some users and imeis? For example in id=1 I already have a user and an imei and I want to keep them
Existing fields are erased because you're creating new Phone object with existing id. You should instead retrieve existing Phone model and update it:
phone_model = Phone.objects.get(id=1)
phone_model.num_calls = NumberOfCalls
phone_model.time_btwn_calls = TimeBetweenCalls
phone_model.psap = PSAP
phone_model.save()
Or update it using queryset update method:
Phone.objects.filter(id=1).update(
num_calls=NumberOfCalls,
time_btwn_calls=TimeBetweenCalls,
psap=PSAP,
)
First one will touch database twice. Once for loading existing phone, and then for saving new values. Second will touch database only once, updating fields without touching rest of them.
You should be retrieving the existing Phone object and updating it where necessary.
if form.is_valid():
number_of_calls = form.cleaned_data.get('NumberOfCalls')
time_between_calls = form.cleaned_data.get('TimeBetweenCalls')
psap = form.cleaned_data.get('PSAP')
phone = Phone.objects.get(pk=1)
phone.num_calls = number_of_calls
phone.time_btwn_calls = time_between_calls
phone.psap = psap
phone.save()
Even better, make your form a ModelForm, and include only the fields you want to update:
class Send2phone(forms.ModelForm):
class Meta:
model = Phone
fields = ['num_calls', 'time_btwn_calls', 'psap']
now your view is just:
phone = Phone.objects.get(pk=1)
if request.method == 'POST':
form = Send2tcu(request.POST, instance=phone)
if form.is_valid():
form.save()
return redirect(reverse('gracias'))
else:
form = Send2tcu(instance=phone)
return render(request, 'heroconfigurer/heroconfigurer.html', {'form': form})

Django set default form values

I have a Model as follows:
class TankJournal(models.Model):
user = models.ForeignKey(User)
tank = models.ForeignKey(TankProfile)
ts = models.IntegerField(max_length=15)
title = models.CharField(max_length=50)
body = models.TextField()
I also have a model form for the above model as follows:
class JournalForm(ModelForm):
tank = forms.IntegerField(widget=forms.HiddenInput())
class Meta:
model = TankJournal
exclude = ('user','ts')
I want to know how to set the default value for that tank hidden field. Here is my function to show/save the form so far:
def addJournal(request, id=0):
if not request.user.is_authenticated():
return HttpResponseRedirect('/')
# checking if they own the tank
from django.contrib.auth.models import User
user = User.objects.get(pk=request.session['id'])
if request.method == 'POST':
form = JournalForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
# setting the user and ts
from time import time
obj.ts = int(time())
obj.user = user
obj.tank = TankProfile.objects.get(pk=form.cleaned_data['tank_id'])
# saving the test
obj.save()
else:
form = JournalForm()
try:
tank = TankProfile.objects.get(user=user, id=id)
except TankProfile.DoesNotExist:
return HttpResponseRedirect('/error/')
You can use Form.initial, which is explained here.
You have two options either populate the value when calling form constructor:
form = JournalForm(initial={'tank': 123})
or set the value in the form definition:
tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123)
Other solution: Set initial after creating the form:
form.fields['tank'].initial = 123
If you are creating modelform from POST values initial can be assigned this way:
form = SomeModelForm(request.POST, initial={"option": "10"})
https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#providing-initial-values
I had this other solution (I'm posting it in case someone else as me is using the following method from the model):
class onlyUserIsActiveField(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(onlyUserIsActiveField, self).__init__(*args, **kwargs)
self.fields['is_active'].initial = False
class Meta:
model = User
fields = ['is_active']
labels = {'is_active': 'Is Active'}
widgets = {
'is_active': forms.CheckboxInput( attrs={
'class': 'form-control bootstrap-switch',
'data-size': 'mini',
'data-on-color': 'success',
'data-on-text': 'Active',
'data-off-color': 'danger',
'data-off-text': 'Inactive',
'name': 'is_active',
})
}
The initial is definded on the __init__ function as self.fields['is_active'].initial = False
As explained in Django docs, initial is not default.
The initial value of a field is intended to be displayed in an HTML . But if the user delete this value, and finally send back a blank value for this field, the initial value is lost. So you do not obtain what is expected by a default behaviour.
The default behaviour is : the value that validation process will take if data argument do not contain any value for the field.
To implement that, a straightforward way is to combine initial and clean_<field>():
class JournalForm(ModelForm):
tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123)
(...)
def clean_tank(self):
if not self['tank'].html_name in self.data:
return self.fields['tank'].initial
return self.cleaned_data['tank']
If you want to add initial value and post other value you have to add the following :
or None after request.POST
form = JournalForm(request.POST or None,initial={'tank': 123})
If you want to add files or images also
form = JournalForm(request.POST or None,request.FILES or None,initial={'tank': 123})
I hope this can help you:
form.instance.updatedby = form.cleaned_data['updatedby'] = request.user.id
I also encountered the need to set default values in the form during development. My solution is
initial={"":""}
form=ArticleModel(request.POST)
if form.has_changed():
data = {i: form.cleaned_data[i] for i in form.changed_data}
data.update({key: val for key, val in init_praram.items() if key not in form.changed_data})
use form.has_changed ,if form.fields is required you can use this method
How I added the initial to the form:
I read #Sergey Golovchenko answer.
So I just added it to the form in if request.method == 'POST':.
But that's not where you place it, if you want to see what value it got before posting the form.
You need to put it in the form where the else is.
Example here from views.py
def myForm(request):
kontext = {}
if request.method == 'POST':
# You might want to use clean_data instead of initial here. I found something on a stack overflow question, and you add clean data to the Forms.py, if you want to change the post data. https://stackoverflow.com/questions/36711229/django-forms-clean-data
form = myModelForm(request.POST, initial={'user': request.user})
if form.is_valid():
form.save()
return redirect('/')
else:
# you need to put initial here, if you want to see the value before you post it
form = myModelForm(initial={'user': request.user})
kontext['form'] = form
return render(request, 'app1/my_form.html', kontext)

Categories