Save two instances on same model in django - 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)

Related

Creating one object for multiple models in one form

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.

Getting an Attribute error while trying to alter a related model on a django detailView

I have a CarRent model that is a ForeignKey to the Car model, on a CarRent detailView after payment i want to alter the CarRent model as well as the Car model, the code works well for the CarRent model, but altering the Car model results in “AttributeError at /car/requests/4/
type object 'Car' has no attribute 'object'” error
class Car(models.Model):
car_owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='car_owner', on_delete=models.CASCADE)
car_model = models.CharField(max_length=20, blank=True, null=True)
rented = models.BooleanField(default=False)
class CarRent(models.Model):
car = models.ForeignKey(Car, related_name='rented_car', on_delete=models.CASCADE)
driver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='driver_renting', on_delete=models.CASCADE)
active_rent = models.BooleanField(default=False, null=True)
rent_paid = models.BooleanField(default=False)
#login_required
#driver_required(redirect_field_name='app:home')
#csrf_protect
def car_rent_detail_view(request, pk):
object = get_object_or_404(CarRent, id=pk)
# queryset = Car.objects.get(id=pk)
car_object = get_object_or_404(queryset, id=pk)
paystack = PaystackAccount(
settings.PAYSTACK_EMAIL,
settings.PAYSTACK_PUBLIC_KEY,
object.total_cost
)
context = {'object': object, 'pk_public': settings.PAYSTACK_PUBLIC_KEY, 'currency': 'NGN', 'paystack': paystack,
}
if request.method == 'POST':
if paystack.verify_transaction(request.POST['reference']):
messages.success(request, "paystack payment successfull")
car_rented = Car.object.get(pk=pk)
car_rented.rented = True
rent_activation = CarRent.objects.get(pk=pk)
rent_activation.active_rent = True
rent_activation.rent_paid = True
rent_activation.save()
messages.success(request, "Your Rent has successfully being updated")
return render(request, 'app/CarRent_detail.html', context=context)
Any help will be appreciated greatly.
Its a typo, you need to use objects to access manager method, not object. So replace:
car_rented = Car.object.get(pk=pk)
^^^^^^
With:
car_rented = Car.objects.get(rented_car__pk=pk)
You can reverse query from Car model to CarRent via related_name(which is 'rented_car'). More information can be found in documentation.

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

How to update two models with one form?

I want to be able to have a user update two models with one submit button. The first model will house all of the book titles (unique) and pages that users submit. The other will show which users submitted which books.
Ideally, I'd like to do something like this:
if request.method == "POST":
form = AddBookForm(request.POST)
if form.is_valid():
books = form.save(commit=False)
ub = UserBooks()
books.book_title = form.cleaned_data['book_title']
books.book_total_pages = form.cleaned_data['book_total_pages']
ub.user = request.user
ub.book_title = form.cleaned_data['book_title']
ub.save()
books.save()
return redirect('new_book')
But that's giving me the error:
Cannot assign "'Some Book Title'": "UserBooks.book_title" must be a
"Books" instance.
What would be the best way to update two models with one form?
Here are the other files.
models.py
class Books(models.Model):
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
book_title = models.CharField(max_length=100, unique=True)
book_total_pages = models.IntegerField()
class Meta:
ordering = ('-created',)
def __str__(self):
return '{0}'.format(self.book_title)
class UserBooks(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=False, blank=False)
book_title = models.ForeignKey(Books, on_delete=models.CASCADE, null=False, blank=False)
views.py
def new_book(request):
user = request.user
if request.method == "POST":
form = AddBookForm(request.POST)
if form.is_valid():
books = form.save(commit=False)
books.book_title = form.cleaned_data['book_title']
books.book_total_pages = form.cleaned_data['book_total_pages']
books.save()
return redirect('new_book')
else:
form = AddBookForm()
return render(request, 'main/addanewbook.html', {
'form': form,
'allBooks': allBooks,
'userbooks': userbooks,
})
forms.py
class AddBookForm(forms.ModelForm):
class Meta:
model = Books
fields = ('book_title', 'book_total_pages')
you need to change a bit in the view
if form.is_valid():
books = form.save(commit=False)
ub = UserBooks()
books.book_title = form.cleaned_data['book_title']
books.book_total_pages = form.cleaned_data['book_total_pages']
books = books.save()
ub.user = request.user
ub.book_title = books
ub.save()
return redirect('new_book')
this will do it

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