Django form's not valid - django

This question might be asked alot in stackoverflow but i couldn't find the answer.
Take a look at code:
# models.py
class Message(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.CharField(max_length=200, null=True, blank=True)
room = models.ForeignKey(Room, on_delete=models.CASCADE, blank=True, null=True)
posted = models.TimeField(auto_now_add=True)
def __str__(self):
return self.body
views.py:
class RoomInsideView(View):
template_name = 'room/room_inside.html'
form_class = SendMessageForm
room = None
def get(self, request, room_id, room_slug):
self.room = Room.objects.get(id=room_id)
if self.room.is_private:
return redirect('room:private_room_auth', self.room.id)
form = self.form_class()
context = {
'room': self.room,
'form': form,
}
return render(request, self.template_name, context)
def post(self, request, room_id, room_slug):
form = self.form_class(request.POST)
if form.is_valid():
new_msg = Message(body=form.cleaned_data['body'])
new_msg.user = request.user in
all_messages = Message.objects.filter(room=self.room)
messages.error(request, 'form not valid', 'warning')
return render(request, self.template_name, {'form': form, 'message': all_messages})
forms.py:
class SendMessageForm(forms.ModelForm):
class Meta:
model = Message
fields = ('body',)
widgets = {
'body': forms.TextInput(attrs={'class': 'form-control',
'placeholder': 'Send'}),
}
template:
<form method="post" action="" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.body.errors }}
{{ form.body }}
<input type="submit" value="Send" class="btn btn-primary">
</form>
as I added a messages.error if form is not valid it's returning form not valid and I can't find where am I doing wrong

You always add the warning, regardless whether the form is valid or not, this does not make much sense.
That being said, you are writing too much boilerplate code, you can use a CreateView which will eliminate most of the boilerplate code:
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
class RoomInsideView(View):
template_name = 'room/room_inside.html'
form_class = SendMessageForm
success_url = reverse_lazy('name-of-some-view')
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['room'] = get_object_or_404(Room, pk=self.kwargs['room_id'], is_private=False)
return context
def form_invalid(self, form):
messages.error(request, 'form not valid', 'warning')
return super().form_invalid(form)
def form_valid(self, form):
form.instance.room_id = self.kwargs['room_id']
form.instance.user = self.request.user
return super().form_valid(form)
The name-of-some-view should be replaced with the name of the view where the view should redirect to in case of a successful POST request, this is done to implement the Post/Redirect/Get architectural pattern [wiki].
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.

Related

Update view in Django WITHOUT using generic classes

I think I don't understand something fundamental here, but every single tutorial on the topic proposes a solution using either a function or a generic class, and both of them work for me, but I can't figure out how to deal with the issue using just View. So to illustrate where I am at, I am building a very simple blog and want to update data on a single post based on it's id. So what I have is:
models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.CharField(max_length=100)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = '__all__'
urls.py
urlpatterns = [
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update')
]
update_post.html
<form method="post">
{% csrf_token %}
{{ isolated_post.as_p }}
<input type="submit" value="Update">
</form>
and finally views.py
class PostUpdateView(View):
form_class = PostForm
initial = {'key': 'value'}
template_name = "blog/update_post.html"
def get(self, request, pk):
isolated_post = Post.objects.get(pk=pk)
form = self.form_class(instance=isolated_post)
return render(request, self.template_name, {'form': form})
def post(self, request, pk, form):
updated_post = self.form_class(request.POST, instance=form)
if updated_post.is_valid():
updated_post.save()
return HttpResponseRedirect("/post/" + f'{pk}/')
return render(request, self.template_name, {'updated_post': updated_post})
I've tried a lot of things, this time it says that form has not been passed to the post function. but the most I can achieve is the empty form, which throws out an error after I press "update". I think I just do not get the logic of "post" well enough, or how to populate the form with the one i use get function on.
Your view
class PostUpdateView(View):`
form_class = PostForm
template_name = "blog/update_post.html"
def setup(self, request, *args, **kwargs):
self.post_instance = get_object_or_404(Post, pk=kwargs['post_id'])
return super().setup(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
post = self.post_instance
form = self.form_class(instance=post)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
post = self.post_instance
form = self.form_class(request.POST, instance=post)
if form.is_valid():
new_post = form.save(commit=False)
new_post.save()
return redirect('your dynamic url', post.id)

DJANGO: forms dont show error to user (def post + ListView)

can you help me?
I can't fix problem: my don't show error validation
when I write not unique slug at form -> no error at form
I think problem at use def post() or return redirect after validations form.
I try many different solutions but nothing helps.
Maybe you should use a non-standard way to report an error?
models.py
class ShortUrl(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Автор URL', null=True)
url = models.CharField('Ссылка', max_length=200)
slug = models.SlugField('Короткое имя ссылки', unique=True, max_length=20)
def __str__(self):
#return self.slug
return f"Короткая ссылка: {self.user} >> {self.slug}"
​
class Meta:
verbose_name = 'Ссылка'
verbose_name_plural = 'Ссылки
forms.py
class ShortURLForm(forms.ModelForm):
slug = forms.SlugField(
label='Название URL',
required=True,
widget=forms.TextInput(attrs={'placeholder': 'Укажите уникальный URL'})
)
url = forms.CharField(
label='Ссылка',
required=True,
widget=forms.TextInput(attrs={'placeholder': 'Ссылка которую нужно сократить'})​
)
class Meta:
model = ShortUrl
fields = ['user', 'url', 'slug']
widgets = {'user': forms.HiddenInput()}
views.py
class ShortURLPage(LoginRequiredMixin, ListView):
model = ShortUrl
template_name = 'main/shorts.html'
context_object_name = 'shorts'
​
def get_context_data(self, *, object_list=None, **kwargs):
ctx = super(ShortURLPage, self).get_context_data(**kwargs)
ctx['form'] = ShortURLForm()
userurls = ShortUrl.objects.filter(user=self.request.user)
ctx['shorts'] = userurls
ctx['title'] = 'Добавление ссылок'
return ctx
​
def post(self, request, *args, **kwargs):
post = request.POST.copy()
post['user'] = request.user
request.POST = post
form = ShortURLForm(request.POST)
​
if form.is_valid():
slug = form.cleaned_data['slug']
url = form.cleaned_data['url']
form.save()
​
return redirect('shorts')
shorts.html
<form method="post" class="form">
{% csrf_token %}
{{ form }}
<button class="button" type="submit">Создать ссылку</button>
</form>
urls.py
urlpatterns = [
path('', views.homepage, name='home'),
path('about/', views.about, name='about'),
path('shorts/', views.ShortURLPage.as_view(), name='shorts'),
path('shorts/<str:slug>/', views.urlRedirect, name='redirect'),
]
Ok, you're not so far away with accomplishing what you want.
Generally your post method should look like this:
def post(self, request, *args, **kwargs):
post = request.POST.copy()
post['user'] = request.user
request.POST = post
form = ShortURLForm(request.POST)
​
if form.is_valid():
slug = form.cleaned_data['slug']
url = form.cleaned_data['url']
form.save()
else:
context = {
'form': form,
}
return render(
request,
self.template_name,
context,
)
return redirect('shorts')
Then, you should write your shorts.html template like this:
<form method="post" class="form">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.errors }}
{{ form }}
<button class="button" type="submit">Создать ссылку</button>
</form>

How to point django form to pk?

I am trying to make a form that is dynamically selected by a DetailView object.
I want to click the DetailView link and be taken to a form whose primary key is the same as the primary key from my detail view. When I attempt to do this I get an error. How can I do this? Is their a prebuilt library that will assist me?
My Model:
'''
class MemberStudent(models.Model):
instructor = models.ForeignKey(Teachers, on_delete=models.CASCADE)
name = models.CharField(max_length=50, default="Doe")
age = models.IntegerField(default=99)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('student_detail', args=[str(self.id)])
class BehaviorGrade(models.Model):
SEVERITY = [
('Bad','Bad Behavior'),
('Good','Good Behavior'),
('Danger','Dangerous'),
]
LETTER_GRADE = [
('A','A'),
('B','B'),
('F','F'),
]
studentName = models.ForeignKey(MemberStudent, on_delete=models.CASCADE) #need a link to student name
eventGrade = models.CharField(max_length=1, choices=LETTER_GRADE)
eventSeverity = models.CharField(max_length=15, choices=SEVERITY)
eventTitle = models.CharField(max_length=15)
eventDescription = models.TextField()
def __str__(self):
return self.eventTitle
'''
My Views:
'''
class StudentDetailView(FormMixin,DetailView):
model = MemberStudent
template_name = 'student_detail.html'
form_class = BehaviorForm
def get_success_url(self):
return reverse('student_detail', kwargs={'pk':self.object.pk})
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
form.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
return super().form_valid(form)
'''
my forms.py:
'''
class BehaviorForm(ModelForm):
class Meta:
model = BehaviorGrade
fields = ['eventGrade',
'eventSeverity','eventTitle',
'eventDescription']
'''
my url:
'''
path('students/<int:pk>', StudentDetailView.as_view(), name='student_detail'),
'''
my htmltemplate(its a detailview with a form below on the same page):
'''
{{ object.id }}
{{ object.name }}
{{ object.age }}
{{ object.instructor }}
<form action="{% url 'student_detail' object.id %}" method="POST">
{{ form }}
{% csrf_token %}
<input type="submit" value="REVIEW">
</form>
'''
error message when form submitted:
"IntegrityError at /student/students/1
NOT NULL constraint failed: Students_behaviorgrade.studentName_id"
The form also is sent without the PK I have requested in my form code.
the django error log shows the PK is never sent
here is the log message on a test of dummy data:
params [None, 'A', 'Bad', 'asdf', 'asdf']
That's because you have to manually set the member student id on the behavior garde.
You can do it like this in the post function of your view :
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
behavior_grade = form.save(commit=False)
behavior_grade.studentName = self.object
return self.form_valid(form)
else:
return self.form_invalid(form)
You can also take a look at this method to use the form_valid function to do it :
## Include the instance object before saving
def form_valid(self, form):
form.instance.studentName = self.object
return super().form_valid(form)
The solution was to to call the primary key in the model method. Then to save to the correct model object I called .pk on my object. I'm cannot elegantly explain this solution however upon testing several different scenarios it works.
'''
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
behavior_grade = form.save(commit=False)
behavior_grade.studentName = self.object #passes unamed form field
behavior_grade.studentName.pk = form.save(commit=True) #calls key
return self.form_valid(form)
else:
return self.form_invalid(form)
'''

__init__() got an unexpected keyword argument 'user' when I try to filter

I would like to filter through the restaurants that the request.user has done. following the docs but i keep getting __init__() got an unexpected keyword argument user when I try to filter
forms.py
from .models import Restaurant
from .models import Item
from django import forms
class LocationCreate(forms.ModelForm):
class Meta:
model = Item
fields = [
'restaurant'
'category',
'food_name'
]
def __init__(self, user=None, *args, **kwargs):
super(ItemCreate, self).__init__(*args, **kwargs)
self.fields['restaurant'].queryset = Restaurant.objects.filter(owner=user)
models.py
class Restaurant(models.Model):
restaurant_name = models.CharField(max_length=250)
restaurant_photo = models.ImageField(upload_to='roote_image')
category = models.ManyToManyField(Category)
timestamp = models.DateTimeField(auto_now_add=True, null=True)
updated = models.DateTimeField(auto_now=True, null=True)
owner = models.ForeignKey(User)
def get_absolute_url(self):
return reverse('post:detail', kwargs={'pk': self.pk})
class Item(models.Model):
restaurant= models.ForeignKey(Roote, on_delete=models.CASCADE, null=True)
food_name = models.CharField(max_length=1000)
category = models.CharField(max_length=250)
owner = models.ForeignKey(User)
def __str__(self):
return self.food_name
def get_absolute_url(self):
return reverse('post:detail', kwargs={'pk': self.pk})
views.py
class ItemCreate(CreateView):
model = Item
fields = ['restaurant','category ', 'food_name ']
success_url = reverse_lazy('post:index')
def form_valid(self, form):
if form.is_valid():
roote = restaurant.objects.filter(owner =self.request.user)
instance = form.save(commit=False)
instance.owner = self.request.user
return super(ItemCreate, self).form_valid(form)
def get_form_kwargs(self):
kwargs = super(ItemCreate, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
detail.html
{% block body %}
<div class="col-sm-6 col-sm-offset-3">
<img src="{{ restaurant.restaurant_photo.url }}" style="width: 250px;" >
<h1>{{ restaurant.restaurant_name }}</h1>
{% for item in restaurant.item_set.all %}
{{ item.food_name }}: {{ item.category}}
<br>
{% endfor %}
The form works without the filter but it brings in every instance of a restaurant that has been made in the web site
full error:
return form_class(**self.get_form_kwargs())
TypeError: __init__() got an unexpected keyword argument 'user'
You forgot to add form_class attribute to your view
class ItemCreate(CreateView):
model = Item
success_url = reverse_lazy('post:index')
form_class = LocationCreate # <- here
def form_valid(self, form):
if form.is_valid():
roote = restaurant.objects.filter(owner=self.request.user)
instance = form.save(commit=False)
instance.owner = self.request.user
return super(ItemCreate, self).form_valid(form)
def get_form_kwargs(self):
kwargs = super(ItemCreate, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
According to the docs, this is the signature of the __init__ method for a ModelForm:
def __init__(self, *args, **kwargs)
See that there is no user? This is what the error message is telling you.
If you want to get the user, try via self.request.user (or something similiar, depending on what middlewares you use).
Hope that helps!

Django class based view and comments

I'm having trouble with adding comments to class based views,
forms.py:
class RequestForm(ModelForm):
class Meta:
model = Request
exclude = ('slug',)
class RequestCommentForm(ModelForm):
class Meta:
model = RequestComment
fields = ['body' ]
models.py:
class Request(models.Model):
title = models.CharField(max_length=250 )
date = models.DateTimeField('Request date', default=timezone.now, editable=False )
department = models.CharField(max_length=2, choices=DEPARTMENT)
support_request = models.TextField('Request', max_length=2500, blank=True)
owner = models.ForeignKey(User,)
slug = models.SlugField(blank=True, editable=False)
def __unicode__(self):
return self.title
views.py:
class RequestDetailView(ModelFormMixin, DetailView):
model = Request
form_class = RequestCommentForm
def get_success_url(self):
return reverse('request-detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(RequestDetailView, self).get_context_data(**kwargs)
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.instance.author = self.request.user
form.instance.request = Request.objects.get(pk=self.object.pk)
form.instance.created = timezone.now
form.save()
Also tried this part with:
self.object = form.save(commit=False)
self.object.author = self.request.user
self.object.request = Request.objects.get(pk=self.object.pk)
self.object.created = timezone.now
self.object.save()
return super(RequestDetailView, self).form_valid(form)
template: request_detail.html comments section .....
{% load bootstrap %}
<form action="{% url 'request-detail' object.id %}" method="post"> {% csrf_token %}
<ul class="form-group">
{{ form|bootstrap }}
</ul>
<input class="btn btn-primary" type="submit" value="Submit" />
</form>
.....
Page renders correctly, but when I submit, no-go with saving comment.
Debug toolbar shows that sql queries are updating the request model, instead of request comment.
Can't figure out how to add simple comment form from different model to detail page.
Any help would be appreciated.
Also, if there is more elegant way off adding comments form to class based view, would love to see it. My google-fu didn't help me to find anything.
I know I'm answering this too late, but maybe someone with the same question, in the future, may benefit.
Class RequestDetailView(DetailView):
model = Request
template_name = 'detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context ['comment'] = RequestComment.objects. all()
context['form'] = RequestCommentForm()
return context
Class CommentCreateView(CreateView):
model = RequestComment
form_class = RequestCommentForm
def get_success_URL(self):
return reverse ('request: detail', kwargs = {'slug':self.object.post.slug})
def form_valid(self, form):
post = get_object_or_404(Request, slug = self.kwargs ['slug'])
Form.instance.post = Request
return super().form_valid(form)
The URL
path('<slug:slug>/add_comment/', CommentCreateView.as_view(), name = add_comment')
And finally the HTML
<form action="{% url 'request:add_comment' request.slug %}" enctype = "multiparty/form-data" method = "post">
</form>
And that's it. I answered using my phone so there's some typos etc. This is my first answer here, I searched stack overflow for answers I'll post links later. I assumed RequestComment foreignkey field is post and creating models with name 'request' is not encouraged in Django.
Wish I could find a way to do this with CBV, but nope.. function view works great...
class RequestDetailView(ModelFormMixin, DetailView):
model = Request
form_class = RequestCommentForm
def get_success_url(self):
return reverse('request-detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(RequestDetailView, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def comments(self):
return RequestComment.objects.filter(request=Request.objects.get(pk=self.object.pk))
def RequestCommentAddView(request, pk):
form = RequestCommentForm(request.POST or None)
if form.is_valid() and pk:
form.instance.author = request.user
form.instance.request = Request.objects.get(pk=pk)
form.save()
return HttpResponseRedirect(reverse('request-detail', kwargs={'pk': pk}))
return HttpResponseRedirect(reverse('request-detail', kwargs={'pk': pk}))