ModelForm в Django не знает, что атрибут уже заполнен - NOT NULL constraint failed: teretana_pretplatnik.first_name
Я делаю приложение Django Gym по этому руководству https://www.youtube.com/watch?v=Mag1n3MFDFk&list=PL2aJidc6QnyOe-fp1m4yKHjcInCRTF53N&index=3. Я хотел написать страницу с формой, которую пользователь (после входа в систему) может заполнить и стать подписчиком. В руководстве он использовал новую модель Enrollment для заполнения данных, но у меня есть модель Pretplatnik (Subscriber на английском), которая имеет отношения OneToOne с моделью django user. Также две другие модели, которые являются FK к Pretplatnik для плана, на который пользователь может подписаться, и модель для тренера, который пользователь может выбрать.
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Oznaka(models.Model):
naziv = models.CharField(max_length=200)
def __str__(self):
return self.naziv
class Plan(models.Model):
naziv=models.CharField(max_length=150)
cijena=models.IntegerField()
max_clanova=models.IntegerField(null=True)
oznake=models.ManyToManyField(Oznaka, default=None, blank=True, related_name='oznake')
def __str__(self):
return '%s' % self.naziv
class Trener(models.Model):
ime=models.CharField(max_length=150)
image=models.ImageField(upload_to='static/assets/img/team', default=None)
def __str__(self):
return self.ime
class Pretplatnik(models.Model):
korisnik=models.OneToOneField(User, on_delete=models.CASCADE)
trener=models.ForeignKey(Trener, on_delete=models.CASCADE, related_name='pretplatnici')
plan=models.ForeignKey(Plan, on_delete=models.CASCADE, null=True)
datum_r=models.DateField(blank=True,null=True)
spol=models.CharField(max_length=25, default=None)
adresa=models.CharField(max_length=150)
def __str__(self):
return str(self.korisnik)
class Pretplata(models.Model):
pretplatnik=models.ForeignKey(Pretplatnik, on_delete=models.CASCADE, null=True)
plan=models.ForeignKey(Plan, on_delete=models.CASCADE, null=True)
class CustomModelName(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
from django import forms
from django.forms import ModelForm
from teretana.models import *
class PretplatnikForm(forms.ModelForm):
plan = forms.ModelChoiceField(queryset=Plan.objects.all(),
to_field_name = 'naziv',
empty_label="Select Plan")
trener = forms.ModelChoiceField(queryset=Trener.objects.all(),
to_field_name = 'ime',
empty_label="Select Trainer")
class Meta:
model = Pretplatnik
fields = ['trener', 'plan', 'datum_r', 'spol', 'adresa']
GENDER_CHOICES = (
('', 'Select a Gender'),
('Female', 'Female'),
('Male', 'Male'),
)
widgets = {
'trener':forms.TextInput(attrs={'class':'form-control','placeholder':'Select Trainer'}),
'plan':forms.TextInput(attrs={'class':'form-control','placeholder':'Select Plan'}),
'datum_r':forms.DateInput(attrs={'class':'form-control','placeholder':'Enter Date of Birth'}),
'spol':forms.Select(choices=GENDER_CHOICES,attrs={'class': 'form-control'}),
'adresa':forms.TextInput(attrs={'class':'form-control','placeholder':'Enter Address'}),
}
labels={
'plan':'Select Plan',
'trener':'Select Trainer',
'datum_r':'Datum_r',
}
views.py
def enroll(request):
if request.method == 'POST':
form = PretplatnikForm(request.POST)
if form.is_valid():
form.save()
form = PretplatnikForm()
return render(request, 'enroll.html', {'form': form})
enroll.html
{% extends 'base.html' %}
{% block title %} Enrollment for Gym {% endblock title%}
{% block head %}
<h2>Join The Best Gym In Rijeka</h2>
<div class="container mt-2">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
{% for message in messages %}
<div
class="alert alert-{{message.tags}} alert-dismissible fade show"
role="alert"
>
<strong></strong> {{message}}
<button
type="button"
class="btn-close"
data-bs-dismiss="alert"
aria-label="Close"
></button>
</div>
{% endfor %}
<form action="/enroll" method="post">{% csrf_token %}
{% csrf_token %}
<div class="form-group">
<label class="form-label">{{form.trener.label_tag}}</label>
{{form.trener}}
</div>
<form action="/enroll" method="post">{% csrf_token %}
{% csrf_token %}
<div class="form-group">
<label class="form-label">{{form.plan.label_tag}}</label>
{{form.plan}}
</div>
<form action="/enroll" method="post">{% csrf_token %}
{% csrf_token %}
<div class="form-group">
{{form.spol}}
</div>
<br>
<form action="/enroll" method="post">{% csrf_token %}
{% csrf_token %}
<div class="form-group">
{{form.adresa}}
</div>
<br>
<form action="/enroll" method="post">{% csrf_token %}
{% csrf_token %}
<div class="form-group">
{{form.datum_r}}
</div>
<br>
<div class="form-group">
<input
type="number"
class="form-control mt-2"
value="{{user.username}}"
name="PhoneNumber"
placeholder="Enter Your Number"
readonly
required
/>
</div>
</div>
<br>
<div class="d-grid gap-2">
<button class="btn btn-warning" type="submit">Enroll</button>
</div>
</form>
</div>
<div class="col-md-3"></div>
</div>
</div>
{% endblock head %}
Он использует HTML-форму для получения данных, но я пробовал, и она не работает для FK (или работает, но я не знаю, как это реализовать), поэтому я попробовал использовать ModelForm. Но теперь я получаю эту ошибку, так как я предполагаю, что ModelForm не знает, что данные пользователя django, которые являются PK для Pretplatnik, уже заполнены (пользователь не может заполнить форму, если не вошел в систему). Я попытался изменить файл views, как я видел в другом вопросе, опубликованном здесь, но затем я получаю ошибку: AttributeError at /enroll
У объекта 'Pretplatnik' нет атрибута 'is_valid'
views.py
def enroll(request):
if not request.user.is_authenticated:
messages.warning(request,"Please Login and Try Again")
return redirect('/login')
if request.method == 'POST':
form = PretplatnikForm(request.POST)
form = form.save(commit=False)
if form.is_valid():
form.save()
form = PretplatnikForm()
return render(request, 'enroll.html', {'form': form})
Это не обязательно ответ, но что-то здесь выглядит странно:
if request.method == 'POST':
form = PretplatnikForm(request.POST)
form = form.save(commit=False) # not sure why this is here
if form.is_valid():
form.save()
Обычно можно увидеть такой шаблон, где commit=False следует за валидацией. Мне неясно, как вы хотели это использовать, или как вы добавляете One-to-One для поля korisnik, но вот пример того, что обычно используется.
if request.method == 'POST':
form = PretplatnikForm(request.POST)
if form.is_valid():
new_sub = form.save(commit=False)
new_sub.korisnik = korisnik_object # queried elsewhere
new_sub.save() # no need to call form.save()