Editing form and saving into database - python

I have a form that takes information about an item and saves it into the database. Im trying to allow users to edit that form with new/different information and save it again. Im having some difficulty trying to get this to work. I think the problem is when Django validates the data it sees that the slug and unique id of the item already exist and doesnt allow it to validate the data but im not completely sure about this as well. Would really appreciate the help. Thanks.
#view
def edit_item(request, item_id):
if request.method == 'POST':
item = Item.objects.get(id=item_id)
form = AddItem(request.POST,instance=item)
if form.is_valid():
item = form.save(commit=False)
item.user = request.user
item.is_active = True
item.slug = slugify(item.name)
item.save()
return HttpResponseRedirect('thanks.html')
else:
form = AddItem(instance=item )
return render_to_response('forsale.html', locals(), context_instance=RequestContext(request))
#form
class AddItem(forms.ModelForm):
name = forms.CharField(label="Title")
class Meta:
model = Item
exclude = ('user','slug','is_active',)
#model
class Item(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=30)
slug = models.SlugField(max_length=50, unique=True)
is_active = models.BooleanField(default=True, blank=True)
image1 = models.ImageField(upload_to='img')
image2 = models.ImageField(upload_to='img', blank=True)
image3 = models.ImageField(upload_to='img', blank=True)
image_caption1 = models.CharField(max_length=200, blank=True)
image_caption2 = models.CharField(max_length=200, blank=True)
image_caption3 = models.CharField(max_length=200, blank=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
quantity = models.IntegerField(default=1)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
shipping_price = models.DecimalField(decimal_places=2, max_digits=6)
categories = models.ManyToManyField(Category)
def save(self, *args, **kwargs):
super(Item, self).save(*args, **kwargs)
if not self.slug:
self.slug = slugify(self.product.title) + "-" + str(self.id)
self.save()

Update your view function like this to return form for get request as well:
def edit_item(request, item_id):
if request.method == 'POST':
item = Item.objects.get(id=item_id)
....
#your existing code
else: #if its GET request
item = Item.objects.get(id=item_id)
form = AddItem(instance=item )
return render_to_response('forsale.html', locals(),
context_instance=RequestContext(request))
Note: you need to handle case when item with item_id does not exists in the DB. In that case do not use instance parameter to instantiate the form.

Related

Django generic create view . Can we add conditions to save data from the form?

This is what my code looks like and I want to add some condition to the Borrower create view like if the stock method of book returns 0 then don't list that book in field while creating a new borrower or if it isn't possible at least throw some error while adding borrower to that book.
models.py:
class Book(models.Model):
id = models.UUIDField(primary_key=True, unique=True,
default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
summary = models.TextField(
max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
help_text='13 Character https://www.isbn-international.org/content/what-isbn')
genre = models.ManyToManyField(
Genre, help_text="Select a genre for this book")
language = models.ForeignKey(
'Language', on_delete=models.SET_NULL, null=True)
total_copies = models.IntegerField()
pic = models.ImageField(blank=True, null=True, upload_to='books')
def stock(self):
total_copies = self.total_copies
available_copies = total_copies - \
Borrower.objects.filter(book=self).count()
if available_copies > 0:
return available_copies
else:
return 0
def __str__(self):
return self.title
class Borrower(models.Model):
id = models.UUIDField(primary_key=True, unique=True,
default=uuid.uuid4, editable=False)
student = models.ForeignKey('Account', on_delete=models.CASCADE)
book = models.ForeignKey('Book', on_delete=models.CASCADE)
issue_date = models.DateField(
null=True, blank=True, help_text='YYYY-MM-DD', default=date.today)
return_date = models.DateField(
null=True, blank=True, help_text='YYYY-MM-DD')
def __str__(self):
return self.student.name.title()+" borrowed "+self.book.title.title()
def fine(self):
today = date.today()
fine = 0
if self.return_date <= today:
fine += 5 * (today - self.return_date).days
return fine
views.py:
class BorrowerView(LoginRequiredMixin, ListView):
model=Borrower
context_object_name='borrowers'
template_name = 'library/borrower_list.html'
def get_context_data(self, **kwargs):
context=super().get_context_data(**kwargs)
if self.request.user.is_admin or self.request.user.is_superuser:
context['borrowers']=context['borrowers']
else:
context['borrowers']=context['borrowers'].filter(student = self.request.user.id)
return context
class BorrowerCreate(LoginRequiredMixin, UserAccessMixin, CreateView):
model=Borrower
permission_required= 'borrowers.add_borrowers'
fields='__all__'
success_url=reverse_lazy('library:borrower-list')
def form_valid(self, form):
form.instance.user=self.request.user
return super(BorrowerCreate, self).form_valid(form)
class BorrowerDetail(LoginRequiredMixin, DetailView):
model=Borrower()
context_object_name='borrower'
template_name='library/borrower.html'
class Book(models.Model):
id = models.UUIDField(primary_key=True, unique=True,
default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
summary = models.TextField(
max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
help_text='13 Character https://www.isbn-international.org/content/what-isbn')
genre = models.ManyToManyField(
Genre, help_text="Select a genre for this book")
language = models.ForeignKey(
'Language', on_delete=models.SET_NULL, null=True)
total_copies = models.IntegerField()
pic = models.ImageField(blank=True, null=True, upload_to='books')
#new, use this to keep track of available books
available_copies = models.IntegerField()
def __str__(self):
return self.title
When any borrower borrows a copy of the book, you will subtract it from the total copies.
class BorrowerCreate(LoginRequiredMixin, UserAccessMixin, CreateView):
model=Borrower
permission_required= 'borrowers.add_borrowers'
fields='__all__'
success_url=reverse_lazy('library:borrower-list')
#remember to get the object using slug or 404
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
book = Book.objects.get(id=instance.book.id)
#get the book id from the form and check if the book is still available, then subtract.
if book.available_copies > 0:
book.available_copies -= 1
book.save()
instance.save()
message.success(self.request, _("successful")
message.error(self.request, _("Book not in stock")
return super(BorrowerCreate, self).form_valid(form)
If user return the book and click returned. you can perform a similar action by adding to available copies.
This is not the solution, you can write a fat model with methods that takes care of both borrowing and return. Like this
def borrow(self):
self.available_copies -= 1
def returned(self):
self.available_copies += 1
You can call these two methods in different views or define a signal that call them using pre_save
Ist:
Instead of defining a new method which you called stock, why not add stock as a field instead of making a database query.
2nd:
calling the class inside the same class is not the best way to make queries inside the class.
3rd:
To add a condition while adding an object, you need to override the save method of the object like this.
def save(self, *args, **kwargs):
# do_something() here.....
# then
return super().save(*args, **kwargs)
The above code will enable you to perform any action before saving the object.
Another way you can do this is inside the form_valid function like this.
def form_valid(self, form):
instance = form.save(commit=False)
# commit = False will make sure that the form is not saved
# then you can now query the database and check conditions like
if Borrower.object.all().count() > 0:
instance.save()
messages.success(self.request, _("saved successfully")
else:
messages.error(self.request, _("Error")
return redirect("URL")

Django Form with dependent ForeignKey forms on one Page

Good day, I am trying to implement django with a dependent foreignkey forms on one page. I have three moodels Store, Product, and ProductClothing. They are all ForeignKey related respectively. Users get to create their store seperately, and are redirected to the store detail view where they will now have to upload Products. But this time, I want them to upload both products and product clothing at once. Below is my models.py and views.py of what I have currently on the store detail view. Also the error I get is:
error.py
NOT NULL constraint failed: product_productclothing.product_id
models.py
class Store(models.Model):
owner = models.ForeignKey(Profile, null=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=100, unique=True)
slug = AutoSlugField(populate_from='name', unique=True)
description = models.CharField(max_length=255, blank=True)
def __str__(self):
return self.name
class Product(models.Model):
store = models.ForeignKey(Store, null=True, on_delete=models.SET_NULL)
owner = models.ForeignKey(Profile, null=True, on_delete=models.SET_NULL)
title = models.CharField(max_length=255)
price = models.DecimalField(max_digits=9, decimal_places=2, verbose_name=_("Regular price"))
class ProductClothing(models.Model):
CLOTHING_GENDER_CHOICES = (
('M', 'Male',),
('F', 'Female',),
('U', 'Unisex',),
)
CLOTHING_TYPE_CHOICES = (
('dress', 'Dresses',),
('fabric', 'Fabrics',),
('shirt', 'Shirts',),
('suit', 'Suits',),
('tshirt', 'T-Shirts',),
('base_layers', 'Base_Layers',),
('blazer', 'Blazers',),
)
product = models.OneToOneField(Product, on_delete=models.CASCADE)
gender = models.CharField(max_length=10, choices=CLOTHING_GENDER_CHOICES, blank=True, null=True)
clothing_type = models.CharField(max_length=255, choices=CLOTHING_TYPE_CHOICES, blank=True, null=True)
def __str__(self):
return self.product.title
views.py
#login_required
def store_dashboard_view(request, slug):
store = get_object_or_404(Store, slug=slug)
new_product = None
product_clothing = None
if request.user.profile == store.owner:
if request.method == 'GET':
product_form = ProductForm()
product_clothing_form = ProductClothingForm()
if request.method == 'POST':
product_form = ProductForm(data=request.POST)
product_clothing_form = ProductClothingForm(data=request.POST)
if product_form.is_valid() and product_clothing_form.is_valid():
new_product = product_form.save(commit=False)
product_clothing_form.save(commit=False)
new_product.store = store
new_product.owner = request.user.profile
product = product_form.save()
product = product
product_clothing_form.product = product
product_clothing_form.save()
print(request.user.first_name)
return redirect('/')
context = {
"object":store,
"form": product_form,
"product_clothing_form": product_clothing_form
}
return render(request, "store/store-dashboard.html", context)
else:
return redirect('store:store-detail-view', slug=store.slug)
Try to Change this part
if product_form.is_valid() and product_clothing_form.is_valid():
new_product = product_form.save(commit=False)
product_clothing_form.save(commit=False)
new_product.store = store
new_product.owner = request.user.profile
product = product_form.save()
product = product
product_clothing_form.product = product
product_clothing_form.save()
print(request.user.first_name)
return redirect('/')
to
if product_form.is_valid() and product_clothing_form.is_valid():
new_product = product_form.save(commit=False)
product_clothing_form = product_clothing_form.save(commit=False)
new_product.store = store
new_product.owner = request.user.profile
new_product.save() #this is correct your mistake was here
print(new_product)
product_clothing_form.product = new_product
product_clothing_form.save()
print(request.user.first_name)
return redirect('/')

How to get the username of the current user and assign it to a certain field in a form in django?

This is my models.py file
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Book(models.Model):
category_choices =(
#("Undefined","Undefined"),
("Action", "Action"),
("Romance", "Romance"),
("Horror", "Horror"),
("Comedy", "Comedy"),
("Adventure", "Adventure"),
("Dramatic", "Dramatic"),
("Crime","Crime"),
("Fantasy","Fantasy"),
)
name = models.CharField(max_length=100)
author = models.CharField(max_length=100, null=True)
content = models.TextField()
price = models.DecimalField(max_digits=5, decimal_places=2)
image = models.ImageField(upload_to= 'photos/%y/%m/%d', blank = True)
category = models.CharField(
max_length = 20,
choices = category_choices,
#default = 'Undefined'
)
publication_year = models.CharField(max_length=4, null=True)
ISBN = models.CharField(max_length=13, null=True, unique=True)
active = models.BooleanField(default= True)
def __str__(self):
return self.name
class Borrow(models.Model):
name = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
book = models.OneToOneField(Book, null=True, on_delete= models.SET_NULL)
period = models.PositiveIntegerField(default=0)
id = models.IntegerField(primary_key=True)
def __str__(self):
return str(self.book)
and this is my forms.py file
from django import forms
from .models import Borrow
class BorrowForm(forms.ModelForm):
class Meta:
model = Borrow
fields = ('name', 'book', 'period')
and this is the function in my views.py file that renders the form
#login_required
def borrowing(request):
momo = BorrowForm()
if request.method == 'POST':
momo = BorrowForm(request.POST)
if momo.is_valid():
instacne = momo.save(commit=False)
instacne.user = request.user.username
instacne.save()
return redirect('profile')
return render(request, 'books/book.html', {'momo': momo})
The role of this function is to render that form and to save the data that user will enter and automatically assign the username of the current user to the field 'name' in form.
I tried alot of things to get the username of the current user and assign it to the field 'name' but nothing works and that field stays blank.
You're using a models.ForeignKey(User) so that table will store a user id, not a username. I'd call this field user and not name, personally.
Therefore you need to provide a user instance to it like this;
#login_required
def borrowing(request):
initial = {}
if request.user.is_authenticated:
initial.update({'name': request.user})
momo = BorrowForm(initial=initial)
if request.method == 'POST':
momo = BorrowForm(request.POST)
if momo.is_valid():
instance = momo.save(commit=False)
instance.user = request.user
instance.save()
If you wanted to easily get the username for a Borrow instance you could do this;
class Borrow(models.Model):
name = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
book = models.OneToOneField(Book, null=True, on_delete= models.SET_NULL)
period = models.PositiveIntegerField(default=0)
id = models.IntegerField(primary_key=True)
def __str__(self):
return str(self.book)
#property
def username(self):
return self.name.username
If you want the form to offer users by username, you can either have the str method of your user model return username, or create custom choices as a tuple of user ID & username in the form __init__

How to get fields of foreign key

In models.py:
class Comment(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
body = models.TextField()
rating = models.FloatField(null=True)
aggregate = models.FloatField(null=True)
date = models.DateTimeField(auto_now_add=True)
class Item(models.Model):
id_item = models.AutoField(primary_key='true')
item_name = models.CharField(max_length=100, null=False)
slug = models.SlugField(max_length=250, blank=True, null=True)
item_description = models.TextField()
item_img = models.ImageField(blank=True, null=True, upload_to="static/item/cover/")
tags = TaggableManager()
In views.py:
def detail(request, slug_text):
details = Item.objects.filter(slug=slug_text)
if details.exists():
reviews = Comment.objects.filter(item=slug_text)
details = details.first()
average = reviews.aggregate(Avg("rating"))["rating_avg"]
average = round(average, 2)
form = CommentForm()
if request.method == "POST":
form = CommentForm(request.POST, author=request.user, item=details)
if form.is_valid():
form.save()
return HttpResponseRedirect(slug_text)
else:
return HttpResponse('<h1>Trang không tồn tại</h1>')
return render(request, 'homepage/detail.html', {'detail': details, 'form': form, 'average': average})
What if I want to get the item.slug = slug_text field in here?
reviews = Comment.objects.filter(item=slug_text)
You not far from it, to get data from a foreignkey you use double under score __ i.e: to get item slug use item__slug see below
reviews = Comment.objects.filter(item__slug=slug_text)
All the best.

Update already existing data in database

I am trying to use the "update ()" to update existing data in the data base of the "ModelC".
Models:
from django.db import models
class ModelA(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
def __str__(self):
return '%s' %(self.id)
class ModelB(models.Model):
observation = models.CharField(max_length=30)
status = models.BooleanField()
relation = OneToOneField(ModelA)
def __str__(self):
return '%s' %(self.relation)
class ModelC(models.Model):
relationA = models.ForeignKey('ModelA', null=True, blank=True)
date_relationA = models.CharField(max_length=10, null=True, blank=True)
user_relationA = models.CharField(max_length=15, null=True, blank=True)
relationB = models.ForeignKey('ModelB', null=True, blank=True)
date_relationB = models.CharField(max_length=10, null=True, blank=True)
user_relationB = models.CharField(max_length=15, null=True, blank=True)
def __str__(self):
return str(self.id)
Views of the ModelA:
def RegModelA(request):
form = ""
user = None
if request.method == "POST":
form = ModelAForm(request.POST)
if form.is_valid():
save = form.save()
date = time.strftime("%d - %m - %Y")
user = request.user.username
create = ModelC.objects.create(relationA=save, date_relationA=date, user_relationA=user, relationB=None, date_relationB=None, user_relationB=None)
return redirect('/')
else:
form = ModelAForm
return render(request, "ModelA.html", {"form":form})
**Result when registering ModelA:**
In ModelA:
Image of the result
In ModelC:
Image of the result
Views of the ModelB:
def RegModelB(request):
form = ""
user = None
if request.method == "POST":
form = ModelBForm(request.POST)
if form.is_valid():
save = form.save()
date = time.strftime("%d - %m - %Y")
user = request.user.username
update = ModelC.objects.filter().update(relationB=save, date_relationB=date, user_relationB=user)
return redirect('/')
else:
form = ModelBForm
return render(request, "ModelB.html", {"form":form})
What I am looking for is that when registering the data of the ModelB, the fields of the relationB in the ModelC are updated.
But I can not find how to obtain the ID of the ModelC, if the relation of the ModelB is equal to the field relationA in the ModelC.
I'm using:
Python 3.5
Django 1.11

Categories