I'm trying to make inline form by using inlineformset_factory but my Image object is not getting saved
models:
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
availability = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self):
return self.name
class Image(models.Model):
file = models.ImageField(upload_to="products_images/", default="static/default.png")
uploaded = models.DateTimeField(auto_now_add=True)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
views:
def CreateNewProductView(request):
context = {}
ProductObj = None
form=ProductForm()
if request.method=='POST':
form = ProductForm(request.POST)
if form.is_valid():
ProductObj = form.save()
print('form is valid, product has been created')
else:
print("form is not valid")
ImageFormset = inlineformset_factory(Product, Image, fields=('file',), extra=1, can_delete=False)
if request.method=='POST':
formset = ImageFormset(request.POST, request.FILES, instance=ProductObj)
if formset.is_valid():
formset.save()
print('formset is valid, product has been created')
else:
print("formset is not valid")
else:
formset = ImageFormset(instance=ProductObj)
if form.is_valid() and formset.is_valid():
return redirect('home')
context = {'form': form, 'formset':formset}
return render(request, 'Ecommerce/test.html', context)
template test.html
{% extends 'base.html' %}
{% block content %}
<form method="POST" action="" id="image-form" style="padding-top:10px;">
{% csrf_token %}
{{form.as_p}}
{{formset}}
{{formset.management_form}}
<button type="submit">submit</button>
</form>
{% endblock content %}
In console I can see "formset is valid, product has been created"
When I printed (request.FILES) i saw <MultiValueDict: {}>. Should it be like that ? In django admin pannel there is no Image objects
What am I doing wrong ?
Add this to your HTML form tag to send files to the server:
enctype="multipart/form-data"
Easy to forget.
Your form will then look like:
<form method="POST" enctype="multipart/form-data" action="" id="image-form" style="padding-top:10px;">
...
Link to Django-docs
views.py
def post(request):
if request.method == 'POST':
form = PostModelForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
# using the for loop i am able to save the tags data.
# for tag in form.cleaned_data['tags']:
# post.tags.add(tag)
images = request.FILES.getlist('images')
for image in images:
ImagesPostModel.objects.create(post=post, images=image)
return redirect('/Blog/home/')
else:
form = PostModelForm(request.POST)
return render(request, 'post.html', {'form': form})
models.py
class PostModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date_time = models.DateTimeField(auto_now_add=True)
title = models.TextField(null=True)
body = models.TextField(null=True)
tags = TaggableManager()
def __str__(self):
return str(self.user)
post.html
{% extends 'base.html' %}
{% block content %}
<form action="{% url 'post' %}" enctype="multipart/form-data" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="file" multiple name="images">
<input type="submit">
</form>
{% endblock %}
After giving the input the data is stored in the tags field but, not saving in the database.
I can manually insert data through the admin panel successfully but not as a non-staff user.
I have installed taggit and placed it in the installed_apps in settings.py.
Tags are being saved using post.tags.add(tag) inside for loop. What is the issue with the code?
This is because you use commit=False for the form: then the form has no means to save the many-to-many fields. It is also not necessary to do that, you can work with:
def post(request):
if request.method == 'POST':
form = PostModelForm(request.POST)
if form.is_valid():
form.instance.user = request.user # set the user
post = form.save() # save the form
ImagesPostModel.objects.bulk_create([
ImagesPostModel(post=post, images=image)
for image in request.FILES.getlist('images')
])
return redirect('/Blog/home/')
else:
form = PostModelForm()
return render(request, 'post.html', {'form': form})
Note: Models normally have no Model suffix. Therefore it might be better to rename PostModel to Post.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
I have done what is written on Django Documentation and I have tried several Youtube tutorials, including Stackoverflow's advice. However, I could not make the message "Validation Error" appear on the template. When I click the button to create a post with bad_word, I redirect to the same page, the post isn't saved but the form doesn't show me the message. I tried to save in a print( form.errors ) in the view and the terminal showed the message that I want to see in the template. So I don't know what I'm doing wrong...
view.py
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
title = form.cleaned_data['title']
content = form.cleaned_data['content']
username = User.objects.get(username=f"{request.user}")
new_post = Post(user=username, title=title, content=content, datetime=timezone.now())
new_post.writeOnChain()
cache.expire("cache", timeout=0)
return HttpResponseRedirect("/")
else:
form = PostForm()
return render(request, "api/homepage.html", {'form': form, 'postList': postList})
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'content',)
def clean_title(self):
title = self.cleaned_data['title']
if "bad_word" in title:
raise forms.ValidationError("Error")
return title
def clean_content(self):
content = self.cleaned_data['content']
if "bad_word" in content:
raise forms.ValidationError("Error")
return content
template
<div class="p-3 forms m-3">
<form class="crispy" action="{% url 'homepage' %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" class="btn buttons" value="Create Post">
</form>
</div>
terminal print
<ul class="errorlist"><li>content<ul> class="errorlist"><li>Error</li></ul> </li></ul>
If your form is invalid you always redirect to "/" without form information. Your return redirect needs to be indented with the rest of the "valid" form code.
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
title = form.cleaned_data['title']
content = form.cleaned_data['content']
username = User.objects.get(username=f"{request.user}")
new_post = Post(user=username, title=title, content=content, datetime=timezone.now())
new_post.writeOnChain()
cache.expire("cache", timeout=0)
return HttpResponseRedirect("/") # this line here
else:
My project is here github Project
I'm getting this error.
ValueError at /salesapp/add/ashton-aged-maduro/
The view salesapp.views.add_CartItem didn't return an HttpResponse object. It returned None instead.
I get this error when I click the 'Add to Cart' button on my singleproduct.html template which calls the ProductAddToCart form. The view is add_CartItem.
I also get the error Field is required when I don't initially set the form values. I'm just stuck now.
This is models.py
class Product(models.Model):
itemid = models.CharField(max_length=128, unique=True)
itemname = models.CharField(max_length=128)
brand = models.CharField(max_length=128)
image = models.ImageField(upload_to='static/images/')
notes = models.CharField(max_length=250)
price = models.IntegerField()
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.itemname)
super(Product, self).save(*args, **kwargs)
def __str__(self):
return self.itemname
class CartItem(models.Model):
cart_id = models.CharField(max_length=50)
date_added = models.DateTimeField(auto_now_add=True)
quantity = models.IntegerField(default=1)
itemid = models.ForeignKey('Product', unique=False)
class Meta:
db_table = 'cart_items'
ordering = ['date_added']
def name(self):
return self.product.name
My forms.py
class ProductAddToCartForm(forms.ModelForm):
cart_id = forms.CharField(max_length=50)
date_added = forms.DateTimeField()
quantity = forms.IntegerField()
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
#itemid = models.ForeignKey('Product', unique=False)
class Meta:
model = CartItem
fields = ('cart_id', 'date_added', 'quantity', 'slug', 'itemid', )
My views.py
def add_CartItem(request, product_name_slug):
print('In add_CartItem --------------------')
form = ProductAddToCartForm(request.POST)
p = Product.objects.get(slug=product_name_slug)
form = ProductAddToCartForm(initial={'cart_id': 123, 'date_added':date.date.today(), 'quantity': 1, 'slug':p.slug, 'id':p.id, 'itemid':p.itemid})
form.save(commit=False)
print(form)
print(p.slug)
print(p.id)
print(p.itemid)
if form.is_valid():
print(p)
print('In form.is_valid()--------------------------------')
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
form.save(commit=True)
return index(request)
else:
print(form.errors) #return render(request, 'salesapp/errors.html', {'form': form})
My urls.py
from django.conf.urls import url
from salesapp import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'about/', views.about, name='about'),
url(r'customers/', views.customers, name='customers'),
url(r'products/', views.products, name='products'),
url(r'^add_product/$', views.add_product, name='add_product'),
url(r'^add_customer/$', views.add_customer, name='add_customer'),
url(r'items/(?P<product_name_slug>[\w\-]+)/$', views.show_product, name='show_product'),
url(r'^add/(?P<product_name_slug>[\w\-]+)/$', views.add_CartItem, name='add_CartItem'),
#url(r'^cart/$', views.show_cart, name='show_cart'),
#url(r'^register/$', views.register, name='register'),
#url(r'^login/$', views.user_login, name='login'),
#url(r'^logout/$', views.user_logout, name='logout'),
#url(r'^restricted/', views.restricted, name='restricted'),
]
and my template where I want to display the ProductAddToCartForm but add a product to the CartItem table.
<!DOCTYPE html>
{% extends 'salesapp/base.html' %}
{% load staticfiles %}
{% block title_block %}
{{ product.itemname }}
{% endblock %}
{% block body_block %}
<div>
<div>
<ul style="list-style:none; text-align:center;">
<li style="float:left; width:25%; margin:20px;">
<img src="/{{ product.image }}"/>
<div>
<b>{{ product.itemname }}</b><br/>
Price per cigar:<br/>
<b>${{ product.price }}</b>
<p>{{ product.notes }}</p>
</div>
<form method="post" action="/salesapp/add/{{ product.slug }}/" class="cart">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<br />
<input type="submit" value="Add To Cart" name="submit" alt="Add To Cart" />
</form>
<div class="cb"></div>
</li>
</ul>
</div>
<!-- More code -->
</div>
{% endblock %}
A view function in django should either return a json or a dict or can return a Webpage
You can either do one of following
1) return a json or dict
return {"value1":"value","value2":"value"}
2)redirect to a web page
return redirect('/some/url/')
3)return Http response
return HttpResponse(status=<status code>,content="some content")
4)render a template
t = loader.get_template('myapp/index.html')
c = {'foo': 'bar'}
return HttpResponse(t.render(c, request),
content_type='application/xhtml+xml')
Because you did not return any response to the view,
According to the docs
A view function is simply a Python function that
takes a Web request and returns a Web response.
You need to return to use render method for initial rendering of form and for redirection to another view you can use redirect method of Django.
A view function must return an HttpResponse. For example, if the process was successfull and you dont want to return anything, you can return HttpResponse(status=200)
When a view handles forms, you have to split GET and POST requests. In the GET part you need to instantiate the form without data. In the POST part you fill the form with request.POST data. And this data must have ALL mandatory fields. A typical view function scheme to handle a form is the folowing:
def view(request):
if request.method == "GET":
form = MyForm()
return ...
if request.method == "POST":
form = MyForm(request.POST)
form.save()
return ...
In your template, you have to show all form fields. Then, all form fields will be passed with the request. If you dont, you need to fill the form fields in the view.
first delete the prints, almost when you make a question, are useless in Django
def add_CartItem(request, product_name_slug):
form = ProductAddToCartForm(request.POST)
if request.method == 'POST':
if form.is_valid():
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
ci.save()#you save your model changes, not your form
return HttpResponseRedirect(reverse('your:url'))#your return the success url or the same
else:
#here error, if form isn't valid
else:
form = ProductAddToCartForm(request.POST)
return render(request, 'your/template.html', {'form': form})
That is the correct way to work with forms in Django, first you must make a if statement asking to the browser if is a post request or a normal request, if is post request, take the data from the forms and are adding to the database, if not, Django return a empty template form.
Let me know if you problem solve
We have a blog-like wagtail site and would like to add comments to our post types. Each post is a page object.
We thought about using django-contrib-comments or implement an own plain django comments app with ajax.
But what would be the "all-wagtail-approach" for having a comment functionality on the public wagtail site (only for logged in wagtail users, using ajax)?
We're not looking for a complete implementation, we just need some hints or tips for a wagtail-sensible approach.
Our actual approach is having comments available in in wagtail admin as an InlinePanel on every PostPage. But we're struggling to render a django form for adding new comments on the frontend:
# blog/models.py
class PostPage(RoutablePageMixin, Page):
...field definitions...
#route(r'^comment/new/$')
def add_comment_to_post(self, request):
from .forms import CommentForm
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save()
return render(request, self.template, {
'page': self,
'comment': comment,
})
else:
form = CommentForm()
return render(request, self.template, {
'page': self,
'form': form,
})
content_panels = Page.content_panels + [
...FieldPanels...
InlinePanel('comments', label="Comments"),
]
class Comment(models.Model):
text = models.TextField()
panels = [FieldPanel('text'),]
def __str__(self):
return self.text
class Meta:
abstract = True
class PostPageComments(Orderable, Comment):
page = ParentalKey('PostPage', related_name='comments')
# blog/forms.py
from django import forms
from .models import PostPageComments
class CommentForm(forms.ModelForm):
class Meta:
model = PostPageComments
fields = ['text']
# blog/templates/blog/post_page.html
{% extends "core/base.html" %}
{% load wagtailcore_tags %}
{% block content %}
{% include 'stream/includes/post_list_item.html' with include_context="post_detail_page" post=self %}
<h3>New comment</h3>
<form method="post" action="comment/new/" id="comment-new">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Send</button>
</form>
{% endblock %}
But: The form ({{ form.as_p }}) is not rendered - any hints? A django admin for PostPageComments works as expected.
Some minor changes to my model and template and I have my simple comment form (code not mentioned is unchanged in relation to the question; unrelated code omitted for brevity):
# blog/models.py
class PostPage(Page):
def serve(self, request):
from .forms import CommentForm
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.page_id = self.id
comment.save()
return redirect(self.url)
else:
form = CommentForm()
return render(request, self.template, {
'page': self,
'form': form,
})
class Comment(models.Model):
text = models.TextField()
class Meta:
abstract = True
class PostPageComments(Orderable, Comment):
page = ParentalKey('PostPage', related_name='comments')
# blog/templates/blog/post_page.html
<form method="post" id="comment-new">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Send</button>
</form>