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()
Related
URLS.py
urlpatterns = [
path('stock/update/<int:pk>/', views.StockUpdateView, name='stock-update'),
VIEWS.py
def StockUpdateView(request, pk):
stock = Stock.objects.get(id=pk)
form = StockForm(instance=stock)
if request.method == 'POST':
form = StockForm(request.POST, instance=stock)
if form.is_valid():
form.save()
return redirect('/inventory/stock')
context = {"form": form}
return render(request, 'inventory/update-stock.html', context)
TEMPLATE.html
<form action="" method="POST" class="row g-3">
<div class="col-auto">
<h6>Stock update form:</h6>
{% csrf_token %}
{% for field in form %}
<div class="form-control">
{{field}}
</div>
{% endfor %}
MODEL.py
class Stock(models.Model):
class Meta:
verbose_name_plural = "Stocks"
name = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
product_id = models.ForeignKey('products.Product', on_delete=models.CASCADE)
quantity = models.IntegerField(default=0)
product_price = models.DecimalField(max_digits=19, decimal_places=2, default=0 ,validators=[MinValueValidator(0)])
def get_absolute_url(self):
return reverse("stock-update", args=[self.pk])
def __str__(self):
return f"{self.product_id} {self.quantity} {self.product_price}"
I have changed from using the {{ form }} tag to looping over the fields trying to get the name of the related table instead of the IDs.
I've been trying for days to understand the reason for my error, in the first phase I detected that it was missing {{ formset.management_form }} in the html, which i include and still continues to give the same error:
"ManagementForm data is missing or has been tampered with"
And I don't know if my views are the way they should be done.
Models
class Intervencao(models.Model):
........
class Imagem(models.Model):
intervencao = models.ForeignKey(Intervencao, related_name='intervencaoObjectsImagem', on_delete=models.CASCADE)
imagem = models.ImageField(upload_to=get_image, blank=True, null=True, verbose_name="Fotografia")
def __str__(self, ):
return str(self.intervencao)
<!-- e
Forms
class TabletForm2(forms.ModelForm):
class Meta:
model=Intervencao
fields = __all__
class TabletFormImagem(forms.ModelForm):
imagem = forms.ImageField(required=True)
class Meta:
model=Imagem
fields = ['imagem',]
Views
def project_detail_chefe(request, pk):
ImagemFormSet = modelformset_factory(Imagem,form=TabletFormImagem, extra=3)
instance = Intervencao.objects.get(id=pk)
d1 = datetime.now()
intervencaoForm = TabletForm2(request.POST or None, instance=instance)
formset = ImagemFormSet(request.POST, request.FILES,queryset=Imagem.objects.none())
if request.method == "POST":
if intervencaoForm.is_valid() and formset.is_valid():
instance_form1 = intervencaoForm.save(commit=False)
instance_form1.data_intervencao_fim = d1
instance_form1.save()
images = formset.save(commit=False)
for image in images:
image.intervencao = instance_form1
image.save()
return redirect('index')
else:
intervencaoForm = TabletForm2(request.POST)
formset = ImagemFormSet(queryset=Imagem.objects.none())
context = {
'intervencaoForm':intervencaoForm,
'formset':formset,
}
return render(request, 'tablet/info_chefes.html', context)
HTML
<form method="post" enctype="multipart/form-data" id="gravar">
<div class="row">
<div class="col-md-12">
<table>
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</div>
</div>
</form>
in my case , the problem was the
else:
intervencaoForm = TabletForm2(request.POST)
formset = ImagemFormSet(queryset=Imagem.objects.none())
beacause is in line with the wrong if. all working now
I stuck by an integrity error when I passed comment to my product review page. Help Me through this.
I think the error occurs because of the args which passed through the render function.
My models.py
class Comment(models.Model):
post = models.ForeignKey(List, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
subject = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def __str__(self):
return str(self.user)
views.py
def addcomment(request, id):
list = get_object_or_404(List, pk=id)
form = CommentForm(request.POST or None)
if form.is_valid():
data = Comment()
data.subject = form.cleaned_data['subject']
data.text = form.cleaned_data['text']
print("Redirected.....")
current_user = request.user
data.user_id = current_user.id
data.save()
messages.success(request, "Your Comment has been sent. Thank you for your interest.")
return HttpResponseRedirect(reverse('main:hackathonList', args=[list.id]))
return render(request, 'product.html', {'list': list, 'form': form})
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('subject', 'text')
urls.py
path('addcomment/<int:id>', views.addcomment, name='addcomment'),
template.html
<form action="{% url 'main:addcomment' user.id %}" role="form" method="post">
{% csrf_token %}
<p>{{ form | crispy }}</p>
{% if user.id is not none %}
<button type="submit" class="btn btn-secondary">Comment</button>
{% else %}
You must be logged in to post a review.
{% endif %}
</form>
In views.py instead of data.user_id = current_user.id ie remove this line and add int its place
data.user = current_user
data.post = list
You need to change
<form action="{% url 'main:addcomment' list.id %}" role="form" method="post">
this first. After that, just add a new line before save method call like:
data.post = list
When I update the user profile via the view everything is saving to the db except the image. The forms are validating but image isn't being saved. I can log in the admin portal and successfully add an image to an existing instance. I assume my problem lies in my html template but I can't figure out what it is.
**Btw I've read multiple similiar post but none I believe addresses my issue.
form.py
class EditUserForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
)
class EditProfileForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = UserProfile
fields = (
'description',
'city',
'website',
'phone',
'image',
)
views.py
#transaction.atomic
def edit_profile(request):
if request.method == 'POST':
form = EditUserForm(request.POST, instance=request.user)
form2 = EditProfileForm(request.POST, instance=request.user.userprofile)
if form.is_valid() and form2.is_valid():
form.save()
form2.save()
return redirect(reverse('accounts:view_profile'))
else:
form = EditUserForm(instance=request.user)
form2 = EditProfileForm(instance=request.user.userprofile)
args = {'form': form, 'form2':form2}
return render(request, 'accounts/edit_profile.html', args)
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
edit_profile.html
<div class="container">
{% if form.errors %}
<ol>
{% for field in form %}
<H3 class="title">
<p class="error"> {% if field.errors %}<li>{{ field.errors|striptags }}</li>{% endif %}</p>
</H3>
{% endfor %}
</ol>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form2.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>
If you are uploading files, you must instantiate the form with request.POST and request.FILES.
form2 = EditProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
See the docs on file uploads for more info.
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>