How to save foreign from html form (select)? - django

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.

Related

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

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

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

Access data of a model linked through a OneToOneField model on a form

I want a pre-populated form with the details (e.g. first name and surname) about the profile of a logged-in user, so that they can update them. I have a custom user model with first name and surname in it, and then a profile model which is linked to the user model and extends it, containing some extra information.
I've defined a constant within the profile model which theoretically should get the user's first name and surname.
models.py:
class User(AbstractBaseUser):
email = models.EmailField(verbose_name="email", unique=True, max_length=255)
first_name = models.CharField(max_length=30, blank=True, null=True)
surname = models.CharField(max_length=30, blank=True, null=True)
[...]
objects = UserManager()
[...]
Profile model added
class Profile(models.Model):
user = models.OneToOneField(User, related_name='current_user', on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def surname(self):
return self.user.surname}
def first_name(self):
return self.user.first_name
[...]
views.py:
#login_required
def profile_edit(request):
if request.method == 'POST':
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
if p_form.is_valid():
p_form.save()
messages.success(request, f'Your account has been updated')
[...]
forms.py:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('first_name', 'surname')
template.html:
{% extends "base.html" %}
{% block content %}
<div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ p_form }}
<button class="button" type="submit"> User update</button>
</form>
</div>
{% endblock content %}
When accessing the template via the browser I expect to see the form already populated with the profile's (i.e. user's) first name and surname. Instead, I get a django.core.exceptions.FieldError: Unknown field(s) (surname, first_name) specified for Profile in the shell.
--
Answer
User ruddra's answer works fine, and I've flagged it as the answer to my problem. Nevertheless, declaring two different form objects and printing them out in the template would also work:
views.py:
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
forms.py:
class UserUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'surname')
class ProfileUpdateFormOld(forms.ModelForm):
class Meta:
model = Profile
fields = ('image',)
template.html:
{{ u_form }}
{{ p_form }}
Basically those fields are from User model, they are not in Profile model. So you can change the model class in ProfileUpdateForm to User:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'surname')
Updated answer based on comments:
class ProfileUpdateForm(forms.ModelForm):
first_name = forms.CharField(max_length=255)
surname = forms.CharField(max_length=255)
def __init__(self, *args, **kwargs):
super(ProfileUpdateForm, self).__init__(*args, **kwargs)
self.initial['first_name'] = self.instance.first_name()
self.initial['surname'] = self.instance.surname()
class Meta:
model = Profile
fields = ('first_name', 'surname')
def save(self, *args, **kwargs):
user = self.instance.user
user.first_name = self.cleaned_data.get('first_name')
user.surname = self.cleaned_data.get('surname')
user.save()
return super(ProfileUpdateForm, self).save(*args, **kwargs)
Alternative to override the __init__(...) method is to send the initial data when initiating the form, pass the initial data. For example:
profile = request.user.profile
ProfileUpdateForm(instance=profile, initial={'first_name':profile.first_name(), 'surname': profile.surname()})

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)