Django submitting two forms with a foreign key - python

I am trying to submit two forms in one template. With one model key being the foreign key for the other model. The foreign key will be set after the POST has been done.
class CustomerSystem(models.Model):
---Some fields---
class MN(models.Model):
--some fields--
customer_system_serial_number = models.ForeignKey(CustomerSystem, on_delete= models.CASCADE)
This is how my models.py looks like
The template file is a standard template with 'form' variable
In forms.py I have excluded the customer_system_serial_number from the MN model.
This is what I am attempting in views
#login_required
def add_customers(request):
if request.method == 'POST':
form_cs = CustomerSystemForm(request.POST, prefix='cs')
form_mn = MNForm(request.POST, prefix='mn')
if form_cs.is_valid() and form_mn.is_valid():
model_instance = form_cs.save(commit=False)
model_instance.save()
print(model_instance)
form_mn.customer_system_serial_number = model_instance
form_mn.save()
return HttpResponseRedirect('/database/customers')
else:
form_cs = CustomerSystemForm(request.POST, prefix='cs')
form_mn = MNForm(request.POST, prefix='mn')
return render(request, 'database/customer_system.html', {'form_cs': form_cs, 'form_mn': form_mn})
The error I am getting is the not Integrity error/not null error. I can see that the model_instance is being saved since it prints a valid object. I am trying to do something like how to set foreign key during form completion (python/django) However I am clearly missing something probably very basic. Any help is appreciated

You're doing the commit=False on the wrong form, and you're setting the foreignkey onto the MN form, not the model instance.
model_instance = form_cs.save()
mn_instance = form_mn.save(commit=False)
mn_instance = customer_system_serial_number = model_instance
mn_instance.save()
return HttpResponseRedirect('/database/customers')

Related

Setting an initial value in a Django Form

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.

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 - form.cleaned_data[] for all fields in model

I am using django to digitalise a form. This form is a little bit complex, and there are a lot of fields in it. I was wondering if Django could do form.cleaned_data[] for all fields, in stead of declaring variables like obj.fieldname = form.cleaned_data['fieldname'] for each field apart.
I tried it with a forloop in the views.py, but that won't work
This is the forloop I'm talking about:
def get_form_naw(request):
if request.method == 'POST':
form = Form1(request.POST)
if form.is_valid():
for x in Model1():
formname = x.name
o = Model1()
o.formname = form.cleaned_data[formname]
o.save()
else:
form = Form1
return render(request, 'folder/home.html', context=locals())
I'm using a mysql database. My forms are declared like this:
forms.py
class Form1(forms.ModelForm):
class Meta:
model = Model1
exclude = ('id')
You shouldn't have to loop through the fields. You are using a model form, so you should just save the form:
if form.is_valid():
obj = form.save()
...
If you really need to set fields dynamically, you can use setattr.
fieldname = 'username'
value = 'my_user'
setattr(obj, fieldname, value)
you can get the instance of the data before saving like this:
if form.is_valid():
obj = form.save(commit=False) #get instance without saving
# do your thing
obj.save() #save into database

Django relationship in manyToMany models, object has no attribute

I have a problem with creating relationship in m2m models.
models.py
class ECG(models.Model):
procedure= models.ManyToManyField('Procedure')
...
class Procedure(models.Model):
basic_info= models.IntegerField(default=0)
views.py
def newECG(request, procedure_id):
if request.method == 'POST':
form = NewECG(request.POST)
if form.is_valid():
form.save()
info = IncomingProcedure.objects.get(id=procedure_id)
form.procedure.add(info) #HERE IS A PROBLEM
return HttpResponseRedirect('/system/')
else:
form = NewECG()
return render(request, 'system/ecg.html', {'form': form})
forms.py
class NewECG(ModelForm):
class Meta:
model = ECG
exclude = ['procedure']
# fields = '__all__'
fields = [ ... ] # rest fields, except procedure
Procedure already exists. I need to create newECG in relation with procedure. ECG form is saving in db. Problem is when i want to use add() function.
ERROR :'NewECG' object has no attribute 'procedure', exception location: views.py
The ModelForm instance does not have the procedure attribute that is defined on the model. The ECG model instance, however, which is returned by the form's save(...) method, does have it:
ecg_instance = form.save()
info = IncomingProcedure.objects.get(id=procedure_id)
# info = Procedure.objects.get(id=procedure_id)
ecg_instance.procedure.add(info)
You just forgot a step:
def newECG(request, procedure_id):
if request.method == 'POST':
form = NewECG(request.POST)
if form.is_valid():
ecg = form.save()
info = IncomingProcedure.objects.get(id=procedure_id)
ecg.procedure.add(info) #HERE IS A PROBLEM
return HttpResponseRedirect('/system/')
I can't understand why you are trying to add IncomingProcedure.objects to your from in views.py
If you want save it on your database, Should simply do this:
views.py
# ... Your codes:
M = form.save() # save your class instance to M variable
info = IncomingProcedure.objects.get(id=procedure_id)
M.procedure.add(info) # save procedure instance to your object(An ECG model instance)

Django: how to loop through boolean fields and set all to false

So, I have a table called "order" that has a foreign key with the table "store". The order table has a boolean field set by default to false. When a new order is created, I need to loop through the boolean values associated with each individual store and set the other orders to false and the new order to true. Not sure how to create the for loop for that. First I tried to set it into the view, but maybe it can be put into the forms.py file? This my create code so far.
def create(request):
if request.POST:
form = OrderForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/liquors/all')
else:
form = OrderForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('create_order.html', args)
Models provided upon request, you shouldn't need anything except the order model:
class Order(models.Model):
OrderID = models.AutoField(primary_key=True)
storeID = models.ForeignKey(Store)
DateCreated = models.DateField('Date Created', auto_now_add=True)
OrderName = models.CharField('Order Name', max_length=25 )
Active = models.BooleanField()
you must return render_to_response('create_order.html', args) one indentation back. so that validation form will not affected. you don't need to use csrf request if you are using render. I think there is no need to use OrderId in Order model, since it is automattically created. ie id
from django.shortcuts import render
from <appname>.models import Order,Store
def create(request):
if request.POST:
form = OrderForm(request.POST)
if form.is_valid():
#i just assume the storeid variable is exist in request.POST and it is ID.
store_data=Store.objects.get(id=request.POST['storeid'])
#here you might need to update the other orderform to false
Order.objects.filter(storeID=store_data).update(Active=False)
fo=Order(storeID=store_data,Active=True)
frm_order=OrderForm(request.POST,instance=fo)
frm_order.save()
return HttpResponseRedirect('/liquors/all')
else:
form = OrderForm()
return render(request,'create_order.html', {'form':form})
You can use the order object's meta object to loop through the fields. Ex.
for field in order._meta.fields():
if type(field) = django.db.models.fields.BooleanField:
setattr(order, field.name, True)

Categories