Creating new Objects instead of updating - Django - django

I am trying to use Update in Django, but it is not being updated, rather a new object of the model is being created in the database.
For example, if I try to change product label "Laptop" to "Smartphone", Django creates a new product called "Smartphone" instead of updating "Laptop".
This is my code
Model:
class Products(models.Model):
label = models.CharField(max_length=50)
description = models.CharField(max_length=250)
first_price = models.FloatField(null=True)
price = models.FloatField()
quantity = models.IntegerField(default=0)
image = models.ImageField(null=True, blank=True)
def __str__(self):
return self.label
#property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
Form:
class ProductForm(ModelForm):
class Meta:
model = Products
fields = ['label', 'description', 'first_price', 'price', 'quantity', 'image']
View:
#login_required(login_url='login')
def editProduct(request, id):
product = Products.objects.get(id=id)
form = ProductForm(instance=product)
if request.method == 'POST':
form = ProductForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.success(request, 'Product was edited.')
return redirect('index')
context = {
'form': form
}
return render(request, 'store/edit-product.html', context)
Template:
<h3>Edit Product</h3>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.label.label }}
{{ form.label }}
{{ form.description.label }}
{{ form.description }}
{{ form.first_price.label }}
{{ form.first_price }}
{{ form.price.label }}
{{ form.price }}
{{ form.quantity.label }}
{{ form.quantity }}
{{ form.image.label }}
{{ form.image }}
<input type="submit" name="Add product">
{{form.errors}}
</form>
Thank you in advance.

To update an object, it needs to be passed to the form, so:
product = Products.objects.get(id=id)
form = ProductForm(instance=product)
if request.method == 'POST':
form = ProductForm(request.POST, request.FILES, instance=product)
# ^^^ Add this
Otherwise without instance, the form will work as if you are trying to create a new object

Related

How do i make many to many field as a chekbox items in template.?

I have 3 models one is Category(Fields = category_name) and another one is SubSategory(Fields = category(ForeignKey to Category),sub_category).and another model is DummyModel.
# Model
class DummyModel(models.Model):
name = models.CharField(max_length=20)
email = models.EmailField()
category = models.ManyToManyField(Category)
sub_category = models.ManyToManyField(SubCategory)
This is my form
class StartProjectForm(ModelForm):
class Meta:
model = StartProject
fields = (
'name',
'email',
'category',
'sub_category',
)
def __init__(self, *args, **kwargs):
super(StartProjectForm, self).__init__(*args, **kwargs)
self.fields["category"].widget = CheckboxSelectMultiple()
self.fields["category"].queryset = Category.objects.all()
self.fields["sub_category"].widget = CheckboxSelectMultiple()
self.fields["sub_category"].queryset = SubCategory.objects.all()
def save(self, commit=True):
clean = self.cleaned_data.get
name = clean('name')
email = clean('email')
category = clean('category')
sub_category = clean('sub_category')
obj = StartProject()
obj.name = name
obj.email = email
obj.category = category
obj.sub_category = sub_category
obj.save()
This is my view
#view
class StartProjectView(View):
template_name = 'start-project.html'
def get(self, request):
form = StartProjectForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = StartProjectForm(request.POST)
if form.is_valid():
form.save()
form = StartProjectForm()
return render(request, self.template_name, {'form':form})
return HttpResponse("<h2>Done</h2>")
This is my Template
# Template
<form method="post">
{% csrf_token %}
<p>name: <input type="text" name="name"></p>
<p>Email: <input type="text" name="email"></p>
{% for form in form %}
<input type="checkbox" name="category">{{ form.category }}
{% endfor %}
<br>
{% for form in form %}
<input type="checkbox" name="sub_category">{{ form.sub_category }}
{% endfor %}
<button type="submit">Start Now</button>
</form>
I want category and subcategory in my template as checkbox items. How do I do that.?
After digging in your needs, what you are looking for is {{ form.FIELD_NAME }}.
Whit your form {{ form.category }} and {{ form.sub_category }} should work.
Take into account that this only renders the input itself, nor labels or other DOM elements.
Review the docs on 'Rendering fields manually' for more info -> https://docs.djangoproject.com/en/2.2/topics/forms/#rendering-fields-manually

How to display django-simple-history in template from a views.py or other?

I do not know how to display a history in a Django template, nor even how to use it in the Django views.py:
views.py:
class Fournisseur(models.Model):
photo = models.FileField(verbose_name="Photo")
nom_f = models.CharField(max_length=40, verbose_name="Fournisseur")
adresse = models.CharField(max_length=50, verbose_name="Adresse")
email = models.EmailField(verbose_name="Courriel")
contact = models.PositiveIntegerField(verbose_name="Contact")
date_f = models.DateTimeField(auto_now_add=True, verbose_name="Date de création")
history = HistoricalRecords()
def __str__(self):
return self.nom_f
You can try like this:
in views.py
def some_view(request, pk):
if request.method == "GET":
obj = Fournisseur.objects.get(pk=pk)
return render(request, 'template.html', context={'object': obj})
in template.html:
{% for h in object.history.all %}
{{ h }} // history object
{{ h.changed_by }}
{{ h.comment }}
{% endfor %}
For details please see in documentation
def some_view(request, pk):
if request.method == "GET":
obj = Fournisseur.objects.get(pk=pk)
return render(request, 'template.html', context={'object': obj})
{% for h in object.history.all %}
{{ h }} // history object
{{ h.history_object }} // the object of the model
{{ h.history_date }} // the date of history
{{ h.history_user }} // Changed by, the user
{% endfor %}

Django: add fields of the related model (ForeignKey) in the form

I have main model Entertainment and related model EntertainmentCollage. Now i'm doing edditing page for my models in which I will need to transfer for editing both models.
I understand how to transfer one form to the form but with a related model I have difficulty.
class Entertainment(models.Model):
main_photo = models.ImageField(upload_to = 'where/')
place = models.CharField(max_length=200)
description = models.CharField(max_length=200)
event_date = models.DateTimeField(auto_now_add=False, blank=True, null = True)
class EntertainmentCollage(models.Model):
img = models.ImageField(upload_to = 'entertainment/portfolio', blank = True)
album = models.ForeignKey(Entertainment, blank = True, null = True)
forms.py
class WhereCreateForm(ModelForm):
class Meta:
model = Entertainment
fields = ['main_photo','place','description', 'event_date' ]
views.py
def edit_where(request, pk):
place = Entertainment.objects.get(id=pk)
form = WhereCreateForm(instance=place)
if request.user.is_authenticated():
if request.method == "POST":
form = WhereCreateForm(request.POST, request.FILES, instance=place)
if form.is_valid():
form.save()
return redirect('entertainment:where_list')
else:
form = WhereCreateForm()
return render(request, "entertainment/where_edit.html", {'form': form})
html
<form method = "post">
{% csrf_token %}
<p>{{ form.description }}</p>
<p>{{ form.place }}</p>
<p>{{ form.event_date }}</p>
</div>
<div class="col-md-9">
<section class="admin-section">
<div class="row">
<div class="col-md-4 admin__block" is-cover="false">
<div class="cover__wrapper edit__wrapper">
<a class="delete-button">Delete</a>
<a class="make-cover-button">Cover</a>
<img src="img/place-placeholder-1.jpg" alt="">
</div>
</div>
</div>
Add photo
</section>
<section>
<h4>Description</h4>
{{ form.description }}
Save
Cancel
</section>
</form>
As #art06 said in his comment you can use inline formsets.
You can do something like this:
from django.forms import inlineformset_factory
def edit_where(request, pk):
place = Entertainment.objects.get(id=pk)
FormSet2 = inlineformset_factory(Entertainment, EntertainmentCollage)
form = WhereCreateForm(instance=place)
form2 = FormSet2()
if request.user.is_authenticated():
if request.method == "POST":
form = WhereCreateForm(request.POST, request.FILES, instance=place)
form2 = FormSet2(request.POST or None, instance=place)
if form.is_valid():
if form2.is_valid():
form.save()
form2.save()
return redirect('entertainment:where_list')
else:
form = WhereCreateForm()
form2 = FormSet2()
return render(request, "entertainment/where_edit.html", {'form': form, 'form2': form2})
To add new form into template you can use:
{{ form2.management_form }}
{% for frm in form2 %}
{{ frm.as_table }}
{% endfor %}

KeyError 'image' formsets

It's my first time with formsets / images and this is my error:
KeyError at /houses/new/
'image'
This is my code:
models.py
class House(models.Model):
user = models.ForeignKey(User, related_name='houses', on_delete=models.CASCADE)
address = models.CharField(max_length=500)
type = models.CharField(default='House', max_length=100)
stories = models.IntegerField()
square_feet = models.IntegerField()
description = models.TextField()
# Class is for the houses images
class Image(models.Model):
house = models.ForeignKey(House, default=None, related_name="images", on_delete=models.CASCADE)
image = models.ImageField(verbose_name='image')
forms.py
# This is the blueprint for House forms
class AlbumForm(forms.ModelForm):
address = forms.CharField(label="Address:")
type = forms.CharField(label="Type of House (House, Condo, Cottage):")
stories = forms.IntegerField(label="House Stories:")
square_feet = forms.IntegerField(label='Square Feet:')
class Meta:
model = House
fields = ['address', 'type', 'stories', 'square_feet', 'description']
# This is the form for images
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Image
fields = ('image',)
views.py
def post_house(request):
ImageFormSet = modelformset_factory(Image, form=ImageForm, extra=10)
if request.method == 'POST':
house_form = AlbumForm(request.POST)
formset = ImageFormSet(request.POST, request.FILES, queryset=Image.objects.none())
if house_form.is_valid() and formset.is_valid():
post_form = house_form.save(commit=False)
post_form.user = request.user
post_form.save()
for form in formset.cleaned_data:
image = form['image']
photo = Image(house=post_form, image=image)
photo.save()
messages.success(request, "New house listing success")
house = post_form
return redirect('houses:details', house_id=house.pk)
else:
return render(request, 'login.html')
else:
house_form = AlbumForm()
formset = ImageFormSet(queryset=Image.objects.none())
return render(request, 'houses/house_form.html', {'house_form': house_form, 'formset': formset})
house_form.html
{% extends 'base.html' %}
{% block content %}
<br>
<div class="container">
<h4>Post a New Home</h4>
<form id="post_form" method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{{house_form}}
{{ formset.management_form }}
{% for form in formset %}
{{ form }} <br>
{% endfor %}
<input type="submit" name="submit" class="btn btn-success" value="Submit" />
</form>
</div>
{% endblock %}
It must be some relation between my form key and what each form in formset is taking. That being said they're both 'image' so I don't see the problem. Please let me know if you got an idea. Thanks a ton!
Try the below code,
if formset.is_valid():
for form in formset:
data = form.cleaned_data
image = data.get('image')
photo = Image(house=post_form, image=image)
photo.save()

Handle multiple modelforms in one html form

A user will have photos which will be related with their specific album.
So this was the model for that:
class Album(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=200)
pub_date = models.DateTimeField(auto_now_add=True, auto_now=False)
update = models.DateTimeField(auto_now_add=False, auto_now=True)
class Photo(models.Model):
photo_privacy = models.CharField(max_length=1,choices=PRIVACY, default='F')
user = models.ForeignKey(User)
caption = models.TextField()
image = models.ImageField(upload_to=get_upload_file_name)
pub_date = models.DateTimeField(auto_now_add=True, auto_now=False)
Views.py:
def create_album(request, user_name):
user = User.objects.get(username=unquote(user_name))
if request.method=='POST':
pform = AlbumPhotoForm(request.POST, request.FILES)
aform = AlbumForm(request.POST)
p_valid = pform.is_valid()
a_valid = aform.is_valid()
if p_valid and a_valid:
photo = pform.save(commit=False)
album = aform.save(commit=False)
photo.user = user
album.user = user
album.save()
photo.album = album
photo.save()
return HttpResponseRedirect('/'+user.username+'/photos')
else:
return render(request, 'create_album.html',{
'pform':pform,
'aform':aform
})
else:
pform = AlbumPhotoForm()
aform = AlbumForm()
return render(request, 'create_album.html', {
'pform':pform,
'aform':aform
})
And the form:
<form action="/{{ user.username }}/create_album/" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ aform.as_p }}
{{ pform.as_p }}
<input type="submit" value="Create and Upload Album"/>
</form>
This works fine if I only have to upload one file (photo) with that form.
Update:
However what I want to do is, show minimum of three input for uploading photos to the new album:
<form action="/{{ user.username }}/create_album/" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ aform.as_p }}
{{ pform.as_p }}
{{ pform.as_p }}
{{ pform.as_p }}
<input type="submit" value="Create and Upload Album"/>
</form>
When doing so, only the last pform is gets saved. And the other two pform's are ignored. How do I get to save all the three forms of photo (pform) accordingly?
Or is there any other way around? Your help will be much appreciated! Thank you.
Use formsets. They do exactly what you want:
from django.forms.models import formset_factory
PhotoFormSet = formset_factory(AlbumPhotoForm, can_delete=False,
min_num=1, validate_min=True,
max_num=3, validate_max=True,
extra=3)
def create_album(request, user_name):
user = User.objects.get(username=unquote(user_name))
if request.method=='POST':
form = AlbumForm(request.POST)
formset = PhotoFormSet(request.POST, request.FILES)
if all([form.is_valid(), formset.is_valid()]):
album = form.save(commit=False)
album.user = user
album.save()
for photo_form in formset:
if photo_form.cleaned_data:
photo = photo_form.save(commit=False)
photo.album = album
photo.user = user
photo.save()
return redirect('/%s/photos' % user.username )
else:
form = AlbumForm()
formset = PhotoFormSet()
return render(request, 'create_album.html',
{'form': form, 'formset': formset})
And template may look like this:
<form action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<table>
{{ formset }}
</table>
<input type="submit" value="Create and Upload Album"/>
</form>