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.
Related
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).
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.
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')
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.
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)