My django form is never valid. Why is this happening? - django

I have django application where I want user to be able to upload videos. My view looks like this:
class CreateVideo(View):
def post(self, request):
videos = models.Video.objects.all().order_by('-created_on')
form = forms.VideoUploadForm(request.POST)
if form.is_valid():
print('form is valid')
video = form.save(commit=False)
video.save()
print('video uploaded')
else:
print('form not valid')
context = {
'video_list': videos,
'form': form,
}
return redirect('index')
def get(self, request):
videos = models.Video.objects.all().order_by('-created_on')
form = forms.VideoUploadForm()
context = {
'video_list': videos,
'form': form,
}
return render(request, 'videos/upload_video.html', context)
My form:
class VideoUploadForm(forms.ModelForm):
class Meta:
model = Video
fields = ['title', 'description', 'file']
and model:
class Video(models.Model):
video_id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False,
unique=True
)
title = models.CharField(max_length=50, null=True)
description = models.CharField(max_length=500, null=True)
file = models.FileField(null=True)
created_on = models.DateTimeField(default=timezone.now, null=True)
at = models.ForeignKey(at, on_delete=models.CASCADE, null=True)
and my template:
<div>
<form method="post">
{% csrf_token %}
{{ form | crispy }}
<button>Submit!</button>
</form>
</div>
When I click submit button, I get: form not valid in terminal. I want form to be created, but form is just never valid. Where is the problem?

Are you setting the title, description and file fields when submitting the form? For now, they're considered required by your form until you set blank=True on the model's fields (you can read more about it here: https://stackoverflow.com/a/8609425/7196167).

Related

Field 'id' expected a number but got (something with Id)

I am using Django 3.2.6
class FeedbackForm(CreatedMixin,
CommentMixin,
FlagMixin,
models.Model):
name = models.CharField(blank=False,
default="",
null=False,
unique=False,
max_length=500,
verbose_name=gettext("Name:"))
email = models.EmailField(blank=True,
default="",
null=False,
unique=False,
verbose_name=gettext("Email:"))
phone = PhoneField(blank=True,
default="",
null=False,
unique=False,
verbose_name=gettext("Phone:"))
message = models.TextField(blank=False,
default="",
null=False,
verbose_name=gettext("Message:"))
def __str__(self):
return f"{self.name}_{self.email}_{self.phone}"
class FeedbackForm(ModelForm):
class Meta:
exclude = [OmnibusNames.COMMENT.value,
OmnibusNames.FLAG.value,
OmnibusNames.CREATED.value,]
model = FeedbackForm
class FeedbackFormView(FormView):
template_name = 'feedback_forms/feedback_form.html'
form_class = FeedbackForm
class FeedbackFormResult(View):
def post(self, request, *args, **kwargs):
feedback_form_model = apps.get_model(app_label="feedback_forms", model_name="FeedbackForm")
new_feedback = feedback_form_model(request.POST)
new_feedback.save()
return render(request, template_name="feedback_forms/feedback_form.html")
<form id="subscription-form" onsubmit="return false">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
The problem:
{TypeError}Field 'id' expected a number but got <QueryDict: {'csrfmiddlewaretoken': ['W8wfxQvsmQyElPtsdmJFiDJmzbyKZXRYfZS9TAbFGpNZDo22jWaLotUrllHYsDWi'], 'name': ['John'], 'email': ['tad#gmail.com'], 'phone_0': ['+19617828635'], 'phone_1': [''], 'message': ['ddd']}>
Something with id. But I thought that Id is auto-incremented. What can I try to resolve this?
Only a form can process the request.POST, so you should not create a model with request.POST but a form:
from app_name.forms import FeedbackForm
from django.shortcuts import redirect, render
class FeedbackFormResult(View):
def post(self, request, *args, **kwargs):
form = FeedbackForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('name-of-some-view')
return render(request, 'name-of-some-template.html', {'form': form})
I would strongly advise not to give a model and form the same name, that makes not much sense anyway.
Furthermore it might make more sense to work with a FormView [Django-doc] which will implement most of the boilerplate logic.

Using modelForm with CreateView

I have a problem with CreateView. My code is a bit of a frankenstein monster with code from various tutorials, docs, and stackoverflow. I feel like I have misunderstood some fundamental step in the workflow.
Here is the models.py:
class Customer(models.Model):
name = models.CharField(max_length=200, null=False, blank=False)
phone = models.CharField(max_length=50, null=False, blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='customers')
Here is the forms.py:
class CustomerForm(forms.ModelForm):
def clean(self):
super().clean()
name = form.cleaned_data['name'].upper()
form.cleaned_data['name'] = name
class Meta:
model = Customer
fields = ['name', 'phone']
widgets = {
'name': forms.TextInput(attrs={"class": "form-control"}),
'phone': forms.TextInput(attrs={"class": "form-control"}),}
Here is the views.py:
class CustomerCreateView(LoginRequiredMixin, CreateView):
model = Customer
form_class = CustomerForm
context_object_name = 'customer_create'
template_name = 'customers/customer-create.html'
login_url = 'account_login'
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
And lastly here is the template:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
The problem is that when I hit save the page just refreshes and the new object is not created.
What am I doing wrong?
UPDATE:
changing form_valid method to this fixed the problem:
def form_valid(self, form):
form.instance = form.save(commit=False)
form.instance.created_by = self.request.user
form.instance.save()
return super().form_valid(form)
In forms.py
fields = ['name', 'phone']
should be replaced by
fields = ('name', 'phone')

How to save foreign from html form (select)?

I can not save the appropriate value for ForeignKey from the form. The value of ForeignKey is always written to the database, with id = 1.
in html form the values from my model, which is in the database, are output to the select. But when sending data from the form, the values of the fields for which the type of foregonekey are written to the database all the time by id = 1
models.py:
class Sportsman(models.Model):
first_name = models.CharField(max_length=64, blank=True, null=True, default=None, verbose_name='Имя')
last_name = models.CharField(max_length=64, blank=True, null=True, default=None, verbose_name='Фамилия')
gender = models.ForeignKey(Gender, on_delete=models.CASCADE, null=True, blank=True, default=True, verbose_name='Пол')
tournament = models.ForeignKey(Tournament, null=True, blank=True, default=True, on_delete=models.CASCADE, verbose_name='Турнир')
def __str__(self):
return "Спортсмен(ка): %s %s, почта: %s" % (self.first_name, self.last_name, self.email)
class Meta:
verbose_name = 'Спортсмен'
verbose_name_plural = 'Спортсмены'
def save(self, *args, **kwargs):
super(Sportsman, self).save(*args, **kwargs)
forms.py:
class SportsmenForm(forms.ModelForm):
class Meta:
model = Sportsman
exclude = ['created', 'updated']
views.py:
def sportsman(request):
documents = Document.objects.filter(is_active=True)
form = SportsmenForm(request.POST or None)
if request.method == "POST" and form.is_valid():
print(request.POST)
print(form.cleaned_data)
print(form.cleaned_data['email'])
form.save()
new_form = form.save()
return render(request, 'sportsman/sportsman.html', locals())
<QueryDict: {'csrfmiddlewaretoken': ['BzBeiKE82LDcd3tmdzZGSmpOWQatc52SSO7ScEOm7eCVdXsHQWxerSzTZa6KC5xq'], 'first_name': ['test_name'], 'last_name': ['test_name'], '<select name=': ['3']}
>
Try the following, works fine for me.
if request.method == "POST":
form = SportsmenForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
For using the form in the template, you don´t need to declare the inputs, Django does that for you. You just have to use the form as in the following example. Just adding the form fields as a variable will get you the right foreign key options.
<form action="{% url 'your_destination_view' %}" method="post">
{% csrf_token %}
{{ form.first_name }}
{{ form.last_name }}
{{ form.gender }}
{{ form.tournament }}
<div><input type="submit" value="OK" name="form"></div>
</form>
When we use default=True and didn't send any value from form then django save first table value i.e. 1 So remove blank=True and default=True to save null in database.

Why is this ModelForm not valid

I'm new to coding with django, and I'm trying to add comments to my blog app, but I'm having trouble with the validation of this form, it always returns False with form.is_valid(), so the object is never saved
views.py
def blog_post_detail_view(request, slug):
obj = get_object_or_404(BlogPost, slug=slug)
comments = Comment.objects.filter(blog_post=obj)
initial_data = {
"blog_post": obj,
}
form = CommentModelForm(request.POST or None, initial=initial_data)
if form.is_valid():
comment_obj = form.save(commit=False)
comment_obj.user = request.user
comment_obj.save()
form = CommentModelForm()
else:
print('not clean')
context = {
"object": obj,
"comments": comments,
"form": form,
}
template_name = 'blog/detail.html'
return render(request, template_name, context)
forms.py
from django import forms
from .models import Comment
class CommentModelForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content','blog_post']
HTML
<form method='POST' action='.'> {% csrf_token %}
{{ form.as_p }}
<button type='submit'>Send</button>
</form>
models.py
class Comment(models.Model):
content = models.TextField(max_length=300)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, default=1)
blog_post = models.ForeignKey(BlogPost, null=True, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return str(self.user.username)
def __str__(self):
return str(self.user.username)

How do I bypass asking end-user for username while adding a blog post using a form and automatically display logged in user as post author?

I am working on a simple blog which has a model Post. I am trying to create a form for adding blog posts (or adding comments to posts for that matter) so that end users don't have to fill out a form box asking the end user for a username. I would like to be able to just ask for a title and body text for a blog post, and when hit post, it will be posted as the authenticated user.
I tried not including 'user' field in fields in forms, but it seems to be mandatory. Maybe I need to just make it hidden somehow using widgets? In templates, I could maybe write the following:
{% if user.is_authenticated %}
<p>Posting as {{request.user}}</p>
{% else %}
<p><a href={% url 'register' %}Please register to add a blog post</a></p>
{% endif %}
Though I am not sure, I think it would make more sense to have logic in my views.py file.
Here's my 'blog.models' file:
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
text = models.TextField()
published_date = models.DateTimeField(auto_now=True)
# pip install Pillow
image = models.ImageField(null=True, blank=True,
upload_to='photos/%Y/%m/%d/',)
def summary(self):
"""Return a summary for very long posts to
get a glimpse from admin panel"""
return self.text[:100]
def _get_unique_slug(self):
"""Assigns a number to the end of a given slug field to prevent
duplicated slug error. if title of a post is 'ayancik', and another
user creates another post with the same title, second posts' slug
is assigned a value: 'ayancik-2'"""
slug = slugify(self.title)
unique_slug = slug
num = 1
while Post.objects.filter(slug=unique_slug).exists():
unique_slug = '{}-{}'.format(slug, num)
num += 1
return unique_slug
def save(self, *args, **kwargs):
"""Automatically assign slug to objects
by overriding save method"""
self.slug = self._get_unique_slug()
super().save(*args, **kwargs)
def pub_date_pretty(self):
return self.published_date.strftime('%b %e, %Y')
def __str__(self):
"""String representation"""
return self.title
def get_absolute_url(self):
# what does kwargs={'slug':self.slug} really achieve here?
# where would we use 'key-value' pair?
"""Returns the url to access a detailed post"""
return reverse('post-detail', kwargs={"slug": self.slug})
class Meta:
ordering = ['-published_date',]
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE,
related_name='comments')
user = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve_comment(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
'blog.forms' file:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['user', 'title', 'text', 'image']
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('user', 'text',)
and 'blog.views' file:
#login_required
def create_post(request):
if request.method == 'POST':
post_form = PostForm(request.POST)
if post_form.is_valid():
post = post_form.save(request)
post.save()
else:
print(post_form.errors)
else:
# when not POST request, display the empty form
# meaning -> if request.method=='GET':
post_form = PostForm()
context = {
'post_form': post_form,
}
return render(request, 'blog/addpost.html', context)
def add_comment_to_post(request, slug):
post = get_object_or_404(Post, slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post-detail', slug=slug)
else:
form = CommentForm()
template_name = 'blog/add_comment_to_post.html'
return render(request, template_name , {'form': form })
You omit the user in the PostForm:
class PostForm(forms.ModelForm):
class Meta:
model = Post
# no user
fields = ['title', 'text', 'image']
or we can display all fields except 'user' like:
class PostForm(forms.ModelForm):
class Meta:
model = Post
exclude = ('user', )
and then you add the user to the instance in your view:
from django.shortcuts import redirect
#login_required
def create_post(request):
if request.method == 'POST':
post_form = PostForm(request.POST, request.FILES)
if post_form.is_valid():
post = post_form.save(commit=False)
post.user = request.user
post.save()
return redirect('some_view')
else:
post_form = PostForm()
context = {
'post_form': post_form,
}
return render(request, 'blog/addpost.html', context)
The commit=False thus prevents the from from saving the Post object to the database.
Since you want to upload an image, you should pass request.FILES [Django-doc] to the PostForm as well, otherwise you will not process uploaded files. You furthermore need to specify that you use enctype="multipart/form-data" in your form:
<form enctype="multipart/form-data" method="POST" action="{% url 'create_post' %}">
...
</form>
It is better to use a redirect [Django-doc] in case of a successful POST request, since this is the Post/Redirect/Get pattern [wiki]. By rendering a new form, if the user refreshes the page, he/she would create a second post, which is probably not what you want.