I'm very new to Django.
I'm making form to add timetable to specific branch.
So I have 'Branch' model and 'Timetable' model.
'Branch' model is in 'branches' app and 'Timetable' model is in 'schedules' app.
What I'm expecting is when users fill in the form to add timetable, they can choose which branch to add it from select box.
Here is my codes
branches/models.py
from django.core.validators import RegexValidator
from django.db import models
from django.urls import reverse
# Create your models here.
phone_validator = RegexValidator(
regex="\d{2,4}-?\d{3,4}(-?\d{4})?",
message="It's not correct phone number format.",
)
class Branch(models.Model):
srl = models.BigAutoField(primary_key=True, verbose_name="Serial")
name = models.TextField(verbose_name="Branch name")
postcode = models.CharField(max_length=5, verbose_name="Postcode")
address1 = models.TextField(verbose_name="Road address")
address2 = models.TextField(verbose_name="Detail address", blank=True)
phone1 = models.CharField(max_length=13, validators=[phone_validator], verbose_name="Phone 1")
phone2 = models.CharField(max_length=13, validators=[phone_validator], verbose_name="Phone 2", blank=True)
is_opened = models.BooleanField(default=True, verbose_name="Is opened?")
class Meta:
verbose_name = "Branch"
verbose_name_plural = "Branches"
ordering = ["srl"]
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("branches:detail", kwargs={"srl": self.srl})
schedules/models.py
import datetime
from django.conf import settings
from django.db import models
class Timetable(models.Model):
srl = models.BigAutoField(primary_key=True, verbose_name="Serial")
branch_srl = models.ForeignKey(
"branches.Branch",
on_delete=models.CASCADE,
verbose_name="Branch Serial",
)
period = models.DecimalField(
max_digits=2,
decimal_places=0,
verbose_name="Period",
)
period_length = models.DurationField(
default=datetime.timedelta(minutes=50),
verbose_name="Period Length",
)
is_holiday = models.BooleanField(default=False, verbose_name="Is holiday?")
start_time = models.TimeField(default=datetime.time(10, 00), verbose_name="Start time")
end_time = models.TimeField(default=datetime.time(23, 00), verbose_name="End time")
schedules/forms.py
from django import forms
from django.forms import ModelChoiceField, ModelForm
from branches.models import Branch
from schedules.models import Schedule, Timetable
class TimetableForm(ModelForm):
branch_choices = ModelChoiceField(
queryset=Branch.objects.all(),
required=True,
label="Branch",
widget=forms.Select(attrs={"class": "form-select"}),
)
class Meta:
model = Timetable
fields = (
"branch_choices",
"period",
"period_length",
"is_holiday",
"start_time",
"end_time",
)
labels = {
"period": "Period",
"period_length": "Period Length",
"is_holiday": "Is Holiday?",
"start_time": "Start Time",
"end_time": "End Time",
}
widgets = {
"period": forms.NumberInput(attrs={"class": "form-control"}),
"period_length": forms.TimeInput(
format="%H:%M:%S",
attrs={
"class": "form-control",
},
),
"is_holiday": forms.RadioSelect(
choices=[(True, "Yes"), (False, "No")],
attrs={
"class": "form-check-input",
},
),
"start_time": forms.TimeInput(
attrs={
"type": "time",
"class": "form-control",
},
),
"end_time": forms.TimeInput(
attrs={
"type": "time",
"class": "form-control",
},
),
}
Error message is this.
Error message from Django debug mode
--- Edit 0 ---
I forgot to attach schedules/views.py
schedules/views.py
from django.apps import apps
from django.urls import reverse_lazy
from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView
from django.views.generic.list import ListView
from schedules.forms import TimetableForm
from schedules.models import Schedule, Timetable
# Create your views here.
class AddTimetableView(CreateView):
model = Timetable
form_class = TimetableForm
# This should be changed. This is temporary
success_url = reverse_lazy("schedules:index")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_title"] = "Add timetable"
return context
--- Edit 1 ---
Here is my template
schedules/templates/scheduels/timetable_form.html
For your information, I'm using Bootstrap 5 and related CDN link is in main/base.html
{% extends "main/base.html" %}
{% load static %}
{% block content %}
<form action="" method="POST" id="timetable-form" >
{% csrf_token %}
{{ form.non_field_errors }}
<div >
<label for="{{ form.branch_choices.id_for_label }}">{{ form.branch_choices.label }}</label>
<div >{{ form.branch_choices }}</div>
{{ form.branch_choices.errors }}
</div>
<div >
<label for="{{ form.period.id_for_label }}">{{ form.period.label }}</label>
<div >{{ form.period }}</div>
{{ form.period.errors }}
</div>
<div >
<label for="{{ form.period_length.id_for_label }}">{{ form.period_length.label }}</label>
<div >{{ form.period_length }}</div>
{{ form.period_length.errors }}
</div>
<div >
<label for="{{ form.is_holiday.id_for_label }}">{{ form.is_holiday.label }}</label>
<div >
{% for choice in form.is_holiday %}
<div >
{{ choice.tag }}
<label for="{{ choice.id_for_label }}">{{ choice.choice_label }}</label>
</div>
{% endfor %}
</div>
{{ form.is_holiday.errors }}
</div>
<div >
<label for="{{ form.start_time.id_for_label }}">{{ form.start_time.label }}</label>
<div >{{ form.start_time }}</div>
{{ form.start_time.errors }}
</div>
<div >
<label for="{{ form.end_time.id_for_label }}">{{ form.end_time.label }}</label>
<div >{{ form.end_time }}</div>
{{ form.end_time.errors }}
</div>
<div >
<div >
<button type="submit">저장</button>
</div>
</div>
</form>
{% endblock %}
CodePudding user response:
An easier way of setting queryset-related choices on a model form is like by overriding the __init__()
method of the form, rather than manually creating a form field.
class TimetableForm(ModelForm):
...
def __init__(self, *args, **kwargs):
self.branches = Branch.objects.all()
super(TimetableForm, self).__init__(*args, **kwargs)
self.fields['branch_srl'].queryset = self.branches
Furthermore, because you are using a ModelForm
a lot of the labels and field definition should be automatic based on what you have defined on the model. Try to avoid repeating those declarations if you can avoid it.
CodePudding user response:
Thanks to CodenameTim from Django Forum, I found the solution
So I copied the solution below for who experiencing the same issue.
The error is saying that the field branch_srl is null when it’s being inserted so it violates the
NOT NULL CONSTRAINT
. If you check your form, the form doesn’t use that field, but instead it usesbranch_choices
. You need to map the selected value for that field tobranch_srl
somewhere. It can be inTimetableForm.save()
,AddTimetableView.form_valid()
or you could rename the fieldbranch_choices
tobranch_srl
so it saves through to the database.