Creating one object for multiple models in one form - django

This is a project supporting a vendor. I have three models,
Item
DeliveryOrderForm
TableList
Item defines what items are sold by the vendor.
DeliveryOrderForm saves the details of the recipient/buyer.
TableList saves the details of each order's row that is ordered by the buyer.
models.py
class Item(models.Model):
itemID = models.AutoField(unique=True,primary_key=True)
itemPrice = models.DecimalField(default=0,max_digits=19,decimal_places=2)
itemDescription = models.CharField(max_length=30)
#deliveryorder status
class Status(models.IntegerChoices):
pending = 1
disapproved = 2
approved = 3
class DeliveryOrderForm(models.Model):
deliveryOrderID = models.AutoField(unique=True,primary_key=True)
vendorName = models.CharField(max_length=30)
vendorAddress = models.CharField(max_length=200)
recipientName = models.CharField(max_length=30)
recipientPhone = PhoneNumberField(blank=False)
recipientAddress = models.CharField(max_length=200)
deliveryOrderStatus = models.IntegerField(default=Status.pending,choices=Status.choices)
deliveryOrderDate = models.DateTimeField(default=timezone.now)
class TableList(models.Model):
deliveryOrderID = models.ForeignKey(DeliveryOrderForm,on_delete = models.CASCADE)
itemID = models.ForeignKey(Item,on_delete=models.PROTECT)
itemQuantity = models.IntegerField(default=0)
So, in the admin page, creating an object of DeliveryOrderForm is fine. I was also able to display the DeliveryOrderForm along with the TableList.
The issue now trying to create a view that works to CREATE an object of DeliveryOrderForm.
I've tried this :
forms.py
class DOForm(ModelForm):
class Meta:
model = DeliveryOrderForm
fields = '__all__'
views.py
def createDeliveryOrder(request):
model = DeliveryOrderForm
template_name = 'deliveryorder/create.html'
deliveryOrderID = get_object_or_404(
model.objects.order_by('-deliveryOrderID')[:1]
)
formset = inlineformset_factory(DeliveryOrderForm,TableList, fields = [
'itemID',
'itemQuantity',
])
if request.method == 'POST':
form = DOForm(request.POST,prefix = 'deliveryorder')
if form.is_valid() and formset.has_changed():
form.save()
for form in formset:
formset.save()
return reverse_lazy('deliveryorder:index')
else:
if request.method == 'GET':
form = DOForm()
context = {
'deliveryOrderID':deliveryOrderID,
'form':form,
'formset':formset
}
return render(request,template_name,context)
This code can surely display their respective fields, but clicking the submit button does not save their data in the database. I feel like the problem lies in the saving part of the form, but I'm not entirely sure what the fix is.

Related

Save two instances on same model in django

In my case, it a treasury managing app, the task is i want to transfer an amount x from treasury A to treasury B, from bank to cashier or from paypal acc to my bank, i m adding two instances to same table (treasuryitem) but with different details (treasury). in the code bellow, i got two instances but the field treasury doesn t save as i choose, it save same, for ex treasury A in both instances. Also, i would like to fill only first form and treasury of second form, and the other fields of second form have to save automatically (name=name, date=date, debit=credit, credit=debit). Anyone can help pls. thanks in advance
MODEL :
class Treasury(models.Model):
name = models.CharField(max_length=256)
class TreasuryItem(models.Model):
treasury = models.ForeignKey('Treasury', on_delete=models.CASCADE)
date = models.DateField(default=timezone.now)
name = models.CharField(max_length=256)
debit = models.DecimalField(max_digits=20, decimal_places=2, null=True, blank=True)
credit = models.DecimalField(max_digits=20, decimal_places=2, null=True, blank=True)
FORM :
class TreasuryItem1Form(ModelForm):
class Meta:
model = TreasuryItem
fields = "__all__"
class TreasuryItem2Form(ModelForm):
class Meta:
model = TreasuryItem
fields = "__all__"
VIEW:
def TreasuryItem_Create(request, pk):
treasurys = Treasury.objects.all()
treasury = treasurys.get(id=pk)
form1 = TreasuryItem1Form()
form2 = TreasuryItem2Form()
if request.method == 'POST':
form1 = TreasuryItem1Form(request.POST)
form2 = TreasuryItem2Form(request.POST)
if form1.is_valid() and form2.is_valid():
form1.save()
form2.save()
return redirect('treasury_profile', pk)
You don't need two form instances. Simply re-save the instance after sets id as None in order to force a new insert in the database.
def TreasuryItem_Create(request, pk):
treasurys = Treasury.objects.all()
treasury = treasurys.get(id=pk)
form1 = TreasuryItem1Form()
if request.method == 'POST':
form1 = TreasuryItem1Form(request.POST)
if form1.is_valid() and form2.is_valid():
treasury_item = form1.save()
treasury_item.id = None
# modify any other field if need
# treasury_item.name = 'my new name`
treasury_item.save()
return redirect('treasury_profile', pk)

Updating django form without prompting the user to enter their ID

So i'm working on job application portal.
the logic is as follows :
Applicant ---> Applies for ---> Job
Models are (Job, User, Application)
I used the User model from django and i extend it.
Now the dilemma is when i render the ApplicationForm, because i have to update the foreign key and i want it to be updated automatically.
Here is my code :
Models.py
class Job(models.Model):
owner = models.ForeignKey(User,related_name='job_owner',on_delete=models.CASCADE)
title = models.CharField(max_length=100)
#location
job_type = models.CharField(max_length=15,choices=JOB_TYPE)
description= models.TextField(max_length=1000)
published_at = models.DateTimeField(auto_now=True)
vacancy = models.IntegerField(default=1)
salary = models.IntegerField(default=0)
experience = models.IntegerField(default=1)
category = models.ForeignKey('Category',on_delete=models.CASCADE)
icon = models.ImageField(upload_to ='job_icons/',default='job_icons/job.png')
slug = models.SlugField(blank = True,null=True)
class Application(models.Model):
job = models.ForeignKey(Job, related_name="job_applied",on_delete=models.CASCADE)
applicant = models.ForeignKey(User,related_name='job_applicant',on_delete=models.CASCADE)
first_name= models.CharField(max_length=40)
last_name= models.CharField(max_length=40)
email = models.EmailField(max_length=60)
website = models.URLField()
cv = models.FileField(upload_to='application/')
coverletter = models.TextField(max_length=550)
application_date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.last_name+"\t"+self.first_name
Forms.py
class JobApplication(ModelForm):
class Meta:
model = Application
fields = ['first_name', 'last_name','email', 'website','cv','coverletter']
vews.py
def job_detail(request,slug):
job_specific = Job.objects.get(slug=slug)
form = JobApplication(instance=request.user)
if request.method == 'POST':
form = JobApplication(request.POST,request.FILES)
if form.is_valid():
my_form = form.save(commit=False)
my_form.job = job_specific
Application.applicant.user = request.user
Application.job = job_specific
my_form.save()
context ={'job_specific':job_specific, 'form':form,}
return render(request,"job/job_details.html",context)
So once the user submit their application, i wanted to updated the fields that are "foreign key" without prompting the user.
I do not know how to arrange this in the views.py or if it's even possible this way?
thanks to everyone in advance
So i solved the problem, it was really simple solution:
my_form = form.save(commit=False)
my_form.job = job_specific
my_form.applicant = request.user

Django ManyToManyField Persistence Fails

I have a simple Django 3.1.0 app I need to create in order to assign Tasks with Tags (or assign tags into tasks).
My Model
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
tags = models.ForeignKey('Tag', on_delete=models.SET_NULL, null=True)
class Tag(models.Model):
tag = models.CharField(max_length=30, default="No Tag")
members = models.ManyToManyField('Task')
class Meta:
verbose_name = "tag"
verbose_name_plural = "tags"
My Form
class TaskForm(ModelForm):
class Meta:
model = Task
fields = ['user', 'task', 'tags']
template_name = 'tasks.html'
tags = ModelMultipleChoiceField(
queryset=Tag.objects.values().all(), widget=CheckboxSelectMultiple()
)
My View
def main(request):
model = Task.objects.values().all()
form = TaskForm()
con = {'context': list(model), 'form': form}
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
form.save_m2m()
return redirect('/')
else:
form = TaskForm()
return render(request, "tasks.html", con)
The migrations are successfull, and with the above code, the view shows a checkbox list with the fetched tags, but the problem is that when I hit Submit on the form, the values are not saved/written on the database but the page reloads successfully.
However, if I turn the following:
obj = form.save(commit=False)
form.save_m2m()
into
form.save(commit=True)
#form.save_m2m()
the values are written only from the fields 'user', 'task' - without the 'tags'
It's also funny that what fetches back on the webpage as values of the tags is in the shape of:
[checkbox] {'id': 1, 'tag': 'aks'}
What am I doing wrong? Thanks.
UPDATE after a comment below:
As Abdul Aziz suggested, I had to remove the values() from the queryset. But after that , to make it work, I had to add also:
In the model:
tag = models.CharField(max_length=100, default="No Tags")
and then refer to that one in the form and Vue template.
You have a ForeignKey set to the Tag model on your Task model, when you actually want a ManyToMany relationship between them. Remove the foreign key and set a related_name to the ManyToManyField in the Tag model like so:
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
class Tag(models.Model):
tag = models.CharField(max_length=30, default="No Tag")
members = models.ManyToManyField('Task', related_name="tags")
class Meta:
verbose_name = "tag"
verbose_name_plural = "tags"
Also in your form you have:
tags = ModelMultipleChoiceField(
queryset=Tag.objects.values().all(), widget=CheckboxSelectMultiple()
)
Why are you using values here? Remove it:
tags = ModelMultipleChoiceField(
queryset=Tag.objects.all(), widget=CheckboxSelectMultiple()
)

Setting model user to current user when using inlineformset_factory in Django

I've hit a roadblock that I've spent hours trying to overcome, and I would appreciate some guidance.
I have two models: Listings and Addresses, with the following structure:
class Listings(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
pub_date = models.DateTimeField(auto_now_add=True)
address = models.ForeignKey(Addresses)
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_active = models.BooleanField(default=1)
class Addresses(models.Model):
address1 = models.CharField(max_length=100)
address2 = models.CharField(max_length=100)
address3 = models.CharField(max_length=100)
address4 = models.CharField(max_length=100)
postcode = models.CharField(max_length=8)
I also have these:
class AddressForm(ModelForm):
class Meta:
model = Addresses
fields = ['address1', 'address2', 'address3', 'address4', 'postcode']
class ListingForm(ModelForm):
class Meta:
model = Listings
fields = ['user']
I'm trying to create a form which will add a new listing, however the only information to be entered are the fields in the Addresses model. When the form is submitted, I need a new Listings object and a new Addresses object to be created, but the Listings object must have the 'user' foreign key equal to the id of the current logged-in user.
This is the view:
#login_required(login_url='share:login_view', redirect_field_name='share:addlisting')
def addlisting(request):
ListingInlineFormSet = inlineformset_factory(Addresses, Listings, form=ListingForm, can_delete=False, extra=1)
if request.method == 'POST':
address_form = AddressForm(request.POST)
if address_form.is_valid():
new_address = address_form.save()
listing_formset = ListingInlineFormSet(request.POST, request.FILES, instance=new_address)
if listing_formset.is_valid():
listing_formset.save()
return HttpResponseRedirect(reverse('/listing_added/'))
else:
address_form = AddressForm()
listing_formset = ListingInlineFormSet()
return render(request, 'share/addlisting.html', {
"address_form": address_form,
"listing_formset": listing_formset,
})
In its current state, I get a form containing all the address fields, plus a drop-down user field. When the form is submitted, it creates a new listing with two foreign keys: one for the chosen user, and another for the new address that has just been created. This end result is what I want. HOWEVER, I don't want there to be a drop-down user field in the form - I need the user to be set to the current user. I tried using "exclude = ['user']" in the ListingForm class instead of "fields = ['user']", but this had the result of creating a new address without creating a listing to go with it. So all I got was a new address, and no listing.
What am I doing wrong? I would be so grateful for a solution to this, as I've been banging my head against a wall for a very long time!
In this case I wouldn't use a formset at all. If you need to collect user information to create the new listing, use a single ListingForm and AddressForm, save the new address, and then save the ListingForm with commit=False, assign the user and address to it, and then save the instance.
#login_required(login_url='share:login_view', redirect_field_name='share:addlisting')
def addlisting(request):
if request.method == 'POST':
address_form = AddressForm(request.POST, prefix="address")
listing_form = ListingForm(request.POST, prefix="listing")
if address_form.is_valid() and listing_form.is_valid():
new_address = address_form.save()
new_listing = listing_form.save(commit=False)
new_listing.address = new_address
new_listing.user = request.user
new_listing.save()
return HttpResponseRedirect(reverse('/listing_added/'))
else:
address_form = AddressForm(prefix="address")
listing_form = ListingForm(prefix="listing")
return render(request, 'share/addlisting.html', {
"address_form": address_form,
"listing_form": listing_form
})
If you ever have a many-to-many relationship on Listings you'll need to explicitly save those after saving the instance, as discussed here:
https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#the-save-method

Validation fails on a select box whose contents are added by an Ajax call

This question is related to this one
Remove all the elements in a foreign key select field
I had a foreign key field in my model which was getting pre-populated by its data and I wanted the select list to be empty. I did achieve that but the validation fails when I submit the form.
The error says "Select a valid choice option. 1 is not one of the available choices).
These are my models
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
serving_size = models.ForeignKey(ServingSize)
quantity = models.IntegerField()
order = models.IntegerField()
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
class RecipeIngredientForm(forms.ModelForm):
serving_size = forms.ChoiceField(widget=forms.Select())
class Meta:
serving_size = forms.ChoiceField(widget=forms.Select())
model = RecipeIngredient
fields = ('ingredient', 'quantity', 'serving_size')
widgets = {
'ingredient': forms.TextInput(attrs={'class' : 'recipe_ingredient'}),
'quantity': forms.TextInput(),
'serving_size' : forms.Select(attrs={'class' : 'ddl'}),
}
I get an error on the third line
recipeIngredients = models.RecipeIngredientFormSet(request.POST)
print(recipeIngredients.errors)
objRecipeIngredients = recipeIngredients.save(commit=False)
I want the select box to be empty because it gets populated by an ajax call. Any ideas what to do so the model passes the validation?
EDIT
Serving Size Model
class ServingSize(models.Model):
name = models.CharField(max_length = 255)
unit = models.CharField(max_length = 125)
food_group = models.ForeignKey(FoodGroup)
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
objects = models.Manager()
dal_objects = ServingSizeManager()
def __unicode__(self):
return self.name;
First, why do you have serving_size in the Meta class?
I would use an extra field in the ModelForm and leave out serving_size field altogether.
class RecipeIngredientForm(forms.ModelForm):
mycustomfield = forms.ChoiceField(widget=forms.Select())
class Meta:
model = RecipeIngredient
exclude = ('serving_size', 'created', 'updated') #etc
Then in the view I would manipulate the form to assign a valid ServingSize to the serving_size field.
[EDIT]
Alright, your actual implementation will depend on what you are pulling through ajax and how. But see the following code: -
Your form: -
class CustomRecipeIngredientForm(forms.ModelForm):
recipe = forms.ModelChoiceField( Recipe.objects.all(),
widget=forms.Select(attrs={'class':'customclass',}))
ingredient = forms.ModelChoiceField( Ingredient.objects.all(),
widget=forms.Select(attrs={'class':'recipe_ingredient',}))
my_custom_serving_size_field = forms.ChoiceField(widget=forms.Select(attrs={'class':'ddl',}))
quantity = forms.IntegerField()
order = forms.IntegerField()
class Meta:
model = RecipeIngredient
exclude = ('serving_size', 'created', 'updated',)
Pull your data through ajax into the my_custom_serving_size_field
Your view: -
def my_view(request):
if request.method == 'POST':
form = CustomRecipeIngredientForm(data=request.POST)
if form.is_valid():
new_recipe_ingredient = form.save(commit=False)
new_recipe_ingredient.serving_size = ServingSize.objects.get(pk=form.cleaned_data['my_custom_serving_size_field'])
new_recipe_ingredient.save()
return HttpResponseRedirect(reverse('redirect_to_wherever'))
else:
form = CustomRecipeIngredientForm()
return render_to_response('path/to/my_template.html', {'form': form}, context_instance=RequestContext(request))
Of course, your ServingSize.objects.get() logic will depend on what your are pulling through ajax and how. Try something along these lines and let us know.
Looks like you want a ModelChoiceField, which
Allows the selection of a single model
object, suitable for representing a
foreign key