Handle multiple modelforms in one html form - django

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>

Related

Creating new Objects instead of updating - 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

NOT NULL constraint failed: social_media_app_blogcomment.user_id

I'm making this comment system for my blogs.. I already made the model, the ModelForm and the view to display the comments and the blog. I'm just really confused how to save the comments related to a specific blog. I tried to save the comments with a view but I face an IntegrityError. A little help would be appreciated.
Here's my views.py:
#login_required #View to show the blogs and comments related to it
def readblog(request, blog_pk):
Blog = get_object_or_404(blog, pk=blog_pk)
return render(request, 'social_media/readblog.html', {'Blog':Blog,'Form':CommentForm()})
#login_required #view to save the comments
def commentblog(request,blog_pk):
Blog = get_object_or_404(blog,pk=blog_pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
Form = form.save(commit=False)
Form.Blog = Blog
Form.save()
return redirect('usersfeed')
Urls.py:
path('commentblog/<int:blog_pk>', views.commentblog, name='commentblog'),
path('readblog/<int:blog_pk>', views.readblog, name='readblog'),
HTML Page to write and save comments (along with the blog):
{{ Blog.title }}
<br>
{{ Blog.text }}
<br>
{% if Blog.image %}
<img src="{{ Blog.image.url }}" alt="">
{% endif %}
<br>
<form action="{% url 'commentblog' Blog.id %}" method="post">
{% csrf_token %}
{{ Form.as_p }}
<button type="submit">Comment!</button>
</form>
{% for i in Blog.BlogComment.all %}
{{ i.comment }}
<b>user:{{ i.user }}</b>
<br>
{% endfor %}
Comment's model:
class BlogComment(models.Model): # --run-syncdb <- (Research about this!)
user = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.CharField(max_length=250, null=True)
blog = models.ForeignKey(blog, related_name='BlogComment', on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.comment
Forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = BlogComment
fields = ['comment']
You need to add the user since it's a not null field in your model:
def commentblog(request,blog_pk):
blog_obj = get_object_or_404(blog,pk=blog_pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form_obj = form.save(commit=False)
form_obj.blog = blog_obj
# add user instance
form_obj.user = request.user
form_obj.save()
return redirect('usersfeed')

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()

Overriding url() from ImageField

I have a image field
avatar = models.ImageField(upload_to="avatar", null=True, blank=True)
and using view
class EditView(SuccessMessageMixin, UpdateView):
model = MysiteUser
form_class = MysiteUserForm
pk_url_kwarg = 'pk'
template_name = 'update_form.html'
success_url = '/myprofile/'
success_message = "Zmiany zostaƂy wprowadzone."
def form_valid(self, form):
image_to_del = MysiteUser.objects.get(username=form.instance)
print("Na dysku plik", image_to_del.avatar)
print("Formularz i pole 'avatar'", form.instance.avatar)
if form.instance.avatar != self.request.user.avatar:
self.request.user.avatar.delete()
return super().form_valid(form)
in template form
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="save" />
</form>
I want change link like below
<p><label for="id_avatar">Zdjecie:</label> Now: avatar/n.jpg
on image, how can I do this?

Django foreign key drop down

New to Django and Python and I need a little help with a foreign key drop down. Basically, I have a category model and a image model and I want users to be able to choose which category to put the image in. How do I create a drop down for the category in the image form? Are my views and html correct too? I have had a look online but I can't seem to do it myself. I keep getting errors.
Here are my models:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
def __unicode__(self):
return self.img_name
class Categories(models.Model):
cat_descr = models.CharField(max_length =120, blank=False)
def __unicode__(self):
return self.cat_descr
VIEWS:
#login_required
def upload_images(request):
context = RequestContext(request)
context_dict={}
if request.method == 'POST': # render the form, and throw it back.
# take the form data and process it!
form = UploadImagesForm(request.POST, request.FILES)
if form.is_valid():
print 'form is_valid'
upload_image = form.save(commit=False)
upload_image.img_user = request.user
if 'image' in request.FILES:
upload_image.image =request.FILES['image']
upload_image.save()
return render(request, 'rmb/upload.html', {'upload_image': form})
else:
print form.errors
# Not a HTTP POST, so we render our form using two ModelForm instances.
# These forms will be blank, ready for user input.
else:
form = UploadImagesForm()
context_dict = {'upload_image': form}
all_categories = Categories.objects.order_by('-id')
context_dict['all_categories'] = all_categories
print context_dict
return render_to_response('rmb/upload.html', context_dict, context)
FORMS:
class UploadImagesForm(forms.ModelForm):
#cat_list = ModelChoiceField(queryset=Categories.objects.all())
class Meta:
model = Images
fields=('image','img_name')
HTML:
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ upload_image.as_table }}
<input type="submit" name="submit" value="Upload" />
{% for categories in all_categories %}
<div> {{ categories.id }} </div>
{{ categories.cat_descr }}
<input type="submit" name="submit" value="Upload" />
{% endfor %}
</form>
{% endblock %}
You don't need to insert the HTML for the form manually, just use {{form}} in the template.
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
</form>
{% endblock %}
By default a ForeignKey will be a select field so you shouldn't need to do much else.
As an aside, give your models and fields more appropriate names. We know these are all image fields, because they are on the image and make sure, unless your model is a collection of things, you give it a singular name. Lastly, when using a Foreign Key and item gets an extra field of fieldname_id that is just the ID, whereas fieldname is the property that gives the related item as well.
So instead of:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
Use:
class Image(models.Model):
image = models.ImageField(upload_to='images', blank=False)
name = models.CharField(max_length=120, blank=True)
date = models.DateTimeField(default=now())
user = models.ForeignKey(User)
category = models.ForeignKey(Categories)