I have an Order form where I used formsets because I want the user to dynamically add more products and their quantity.
The user enters the name of the order, and it is validated not to be empty.
How do I validate the formset itself? to check that the product/quantity is not empty
views.py
class OrderCreateView(CreateView):
model = Order
template_name = "orderform.html"
fields = ['name', ]
def get_context_data(self, **kwargs):
data = super(OrderCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['productmetas'] = InlineOrderFormSet(self.request.POST)
return data
def form_valid(self, form):
context = self.get_context_data()
productmetas = context['productmetas']
self.object = form.save(commit=False)
self.object.save()
if productmetas.is_valid():
productmetas.instance = self.object
productmetas.save()
return super(OrderCreateView, self).form_valid(form)
models.py
class ProductMeta(models.Model):
order = models.ForeignKey(Order)
product = models.ForeignKey(Product)
quantity = models.FloatField()
forms.py
InlineOrderFormSet = inlineformset_factory(Order, ProductMeta,
form=OrderAutoCompleteForm,
extra=1)
For anyone interested in the working solution for the question above
adapted from the answer by #neverwalkaloner
def form_valid(self, form):
context = self.get_context_data()
productmetas = context['productmetas']
with transaction.atomic():
self.object = form.save()
if form.is_valid():
if productmetas.is_valid():
productmetas.instance = self.object
productmetas.save()
else:
return super(OrderCreateView, self).form_invalid(form)
return super(OrderCreateView, self).form_valid(form)
Related
i just done comments on my website,and i want to do non-authorized comments,and dont know how to.My models looks like this
class Comment(models.Model):
article = models.ForeignKey(Product, on_delete = models.CASCADE, verbose_name='Page', blank = True, null = True,related_name='comments' )
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.CASCADE, verbose_name='Comment author', blank = True, null = True )
create_date = models.DateTimeField(auto_now=True)
text = models.TextField(verbose_name='Comment text', max_length=500)
status = models.BooleanField(verbose_name='Visible', default=False)
and views.py like this
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.article = self.get_object()
self.object.author = self.request.user
self.object.save()
return super().form_valid(form)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.article = self.get_object()
if self.request.user.is_authenticated:
self.object.author = self.request.user
else:
self.object.author = None
self.object.save()
return super().form_valid(form)
note: remove else block if you get an error
views.py
class DreamHeroView(CreateView):
template_name = 'dream_hero.html'
model = Character
form_class = DreamHeroForm
def get(self, *args, **kwargs):
form = DreamHeroForm()
return render(self.request, 'dream_hero.html', {'form': form})
def post(self, *args, **kwargs):
form = DreamHeroForm(self.request.POST, self.request.FILES or None)
if form.is_valid():
title = form.cleaned_data['title']
description = form.cleaned_data['description']
strenght = form.cleaned_data['strenght']
power = form.cleaned_data['power']
weapon = form.cleaned_data['weapon']
hero_image = form.cleaned_data['hero_image']
slug = form.cleaned_data['slug']
post = Character(
title=title,
description=description,
strenght=strenght,
power=power,
weapon=weapon,
hero_image=hero_image,
slug=slug,
user=self.request.user,
)
post.save()
return redirect('character-post')
return render(self.request, 'dream_hero.html')
views.py
class StoryDetailView(FormMixin, DetailView):
model = Character
template_name = 'story.html'
context_object_name = 'comment_set'
form_class = CharCommentForm
def get_context_data(self, **kwargs):
context = super(StoryDetailView, self).get_context_data(**kwargs)
context['commenting'] = self.object.charactercomment_set.all()
context['pro_pic'] = get_object_or_404(Author, user=self.request.user)
context['charform'] = CharCommentForm()
return context
def get_success_url(self):
return reverse('story-post', kwargs={
'slug': self.object.slug
})
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post = self.get_object()
form.save()
return super().form_valid(form)
form.py
class DreamHeroForm(forms.Form):
title = forms.CharField(widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Name of your hero'
}))
description = forms.CharField(widget=forms.Textarea(attrs={
'class': 'form-control',
'placeholder': 'Story of your Hero'
}))
slug = forms.SlugField()
hero_image = forms.ImageField()
strenght = forms.IntegerField()
power = forms.IntegerField()
weapon = forms.IntegerField()
Django has an UpdateView built in.
from django.views.generic.edit import UpdateView
class MyUpdateView(UpdateView):
model = MyModel
fields = ["field_1", "field_2"] # or use form_class = MyForm
template_name = "myapp/mymodel_update.html"
You'll need a PK in your url for the update view so it can look up the model instance correctly (you can override this by setting lookup_url_kwarg in the class IIRC)
Docs:
https://docs.djangoproject.com/en/3.0/ref/class-based-views/generic-editing/#updateview
I need to add comments in article page. Post request receives data but not saves it.
In models.py:
class TemporaryComment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='temporary_comment')
email = models.EmailField()
message = models.TextField(max_length=1500)
timestamp = models.DateTimeField(auto_now_add=True)
is_approved = models.BooleanField(default=True)
In forms.py:
class CommentModelForm(forms.ModelForm):
class Meta:
model = TemporaryComment
fields = [
'article',
'email',
'message',
]
in views.py:
class ArticleDetailView(FormMixin, DetailView):
model = Article
template_name = 'article.html'
form_class = CommentModelForm
def get_success_url(self):
return reverse_lazy('main:article', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['last_articles'] = Article.objects.filter(is_active=True).order_by('-timestamp')[:10]
context['comments'] = self.object.temporary_comment.filter(is_approved=True)
context['form'] = self.get_form()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return super().form_valid(form)
If i comment out article field in forms.py, i got this error:
null value in column "article_id" violates not-null
constraint. DETAIL: Failing row
contains (18, username#email.com, check message, 2019-01-20 18:35:36.615955+00, t, null).
Help will be gladly accepted. Thanks for your time.
You should comment it out, and set it in form_valid.
def form_valid(self, form):
form.instance.article = self.object
form.save()
return super().form_valid(form)
I have an item. An item can have many reviews. I expect a review to be created within the context of an item. Therefore, I capture the pk from the url and add it to the context.
This is where I get stuck, I'm unsure how to access the context in form_valid and, more importantly, I'm concerned the path I'm trying to go down seems hacky.
Essentially when the user prepares to submit a review, the application will know what item it's in reference to. What's the most pythonic/django-onic way to do this?
Models
class Item(models.Model):
name = models.CharField(max_length=100)
source = models.ForeignKey('Source')
class Review(models.Model):
rating = models.CharField(max_length=30)
value = models.CharField(max_length=30)
date = models.DateField(auto_now_add=True)
comment = models.CharField(blank=True,max_length=100)
item = models.ForeignKey(Item,blank=True)
user = models.ForeignKey(User)
Urls
url(r'^review/create/item/(?P<itempk>\d+)',views.ReviewCreate.as_view(),name='review_create'),
Views
class ReviewCreate(CreateView):
model = Review
fields = ['rating', 'value', 'comment']
def get_context_data(self, **kwargs):
context = super(ReviewCreate, self).get_context_data(**kwargs)
itempk = self.kwargs['itempk']
item = get_object_or_404(Item, pk=itempk)
context['item'] = item
return context
def form_valid(self, form):
review = form.save(commit=False)
review.user = self.request.user
context = super(ReviewCreate, self).get_context_data(**kwargs) '''doesn't work'''
review.item = context['item']
return super(ReviewCreate, self).form_valid(form)
template_name = 'food/review_form.html'
The get_context_data method is meant to return the context for a template, so I agree that calling it in form_valid is a bit hacky.
You could fetch the item in the dispatch method instead and store it as self.item. Then you can retrieve the item in get_context_data and form_valid.
In form_valid you can modify form.instance - that way you don't have to save with commit=False.
class ReviewCreate(CreateView):
model = Review
fields = ['rating', 'value', 'comment']
def dispatch(self, request, *args, **kwargs):
itempk = self.kwargs['itempk']
self.item = get_object_or_404(Item, pk=itempk)
return super(ReviewCreate, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ReviewCreate, self).get_context_data(**kwargs)
context['item'] = self.item
return context
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.item = self.item
return super(ReviewCreate, self).form_valid(form)
template_name = 'food/review_form.html'
I'm practicing Django's generic views, particularly ModelForms
These are my views and models
models.py
class Post(models.Model):
user = models.ForeignKey(User)
post_title = models.CharField(max_length=200)
post_content = models.CharField(max_length=500)
post_date = models.DateTimeField('date posted')
def get_absolute_url(self):
return reverse('user-detail', kwargs={'pk': self.pk})
def __unicode__(self):
return self.post_title
forms.py
class PostForm(forms.ModelForm):
post_title = forms.CharField(
label=u'Title',
widget=forms.TextInput(attrs={'size':64})
)
post_content = forms.CharField(
label=u'Content',
widget=forms.TextInput(attrs={'size':128})
)
class Meta:
model = Post
views.py
class PostCreate(CreateView):
fields = ['post_title', 'post_content']
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(PostCreate, self).dispatch(*args, **kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)
The view seems to work fine, not displaying any errors. However, when I check the Django admin page after submitting a form and saving a post, the post is not saved into the database for some reason.
Any idea why this is happening?
As I can see from your code you use
self.object = form.save(commit=False)
which mean that object will not be saved to database, but you can use it for futher processing. So you should use something like this:
self.object = form.save(commit=False) # Not hit database
self.object.user = self.request.user # Update user
self.object.post_date = datetime.now() # Update post_date
self.object.save() # And finally save your object to database.
Try this.
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)