Django, CBV, form.py, несколько моделей и один шаблон
Здравствуйте уважаемые знатоки! Есть вопрос следующего характера:
есть модель(ли)
models.py
class Person(models.Model):
name = models.CharField(max_length=50)
class Phone(models.Model):
phone = models.CharField(max_length=10)
person_id = ForeignKey(Person, on_delete=models.CASCADE)
Вывести через форму модель - Person - я понимаю как:
forms.py
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = '__all__' #так пишу для упрощения
views.py
class PersonListView(ListView):
model = Person
person_list.html
{% block content %}
{% for person in person_list %}
<p><a href={% url 'person_detail' person.id %}>{{person.name}}</a></p>
<p></p>
{% endfor %}
{% endblock %}
Может быть я не правильно понимаю саму концепцию Forms ? Чтобы просто вывести на страницу записи из БД форму не нужно использовать, достаточно взять из Class-based Views подкласс ListView и через него вывести данные на страницу? Если так, то как правильно вывести детальную информацию по персоне с номерами телефонов, как совместить модели и зависимость? Как я понимаю через DetailView Можно пример как правильно это реализовать с учётом того, что у персон может быть разное число номеров.
И для чего нужны Forms? только для вводов и редактирования записей в БД? Спасибо!
Ответы (2 шт):
В принципе все верно формы нужны для ввода данных пользователем и их последующей проверки. Не обязательно после этого идет сохранение в базу данных, это может быть форма с фильтрами для поиска после которой нужно не сохранить данные, а просто отфильтровать данные на странице.
Так же формы делятся на Form и ModelForm
В Form ты самостоятельно определяешь список полей, которые необходимо ввести, а так же самостоятельно определяешь правила по которым решаешь, что данные пользователя верны, например у тебя может быть три поля A, B, C и для тебя важно, что бы были введены любые два.
ModelForm - развитие концепции форм для ввода данных модели. В них джанго автоматически определяет список и типы полей, на основании описания твоей модели. Так же автоматически прописывает некоторые правила для введенных данных. Например если у тебя поле в модели стоит unique=True, то ModelForm автоматически проверит, что введенные пользователем данные не дублируют другую запись в БД и в противном случае сформирует сообщение об ошибке. Надо сказать, что в ModelForm помимо полей из модели, ты можешь дополнительно добавлять другие поля и форма будет строится на основании данных модели и добавленных тобой полей. Это может быть например в форме регистрации доп. поле, которое отвечает нужно ли отправить пользователю уведомление на e-mail после ввода данных.
Так же ModelForm автоматически умеет сохранять данные введенные пользователем в модель(добавлять или обновлять)
Следующим развитием работы с введенными пользовательскими данными являются Formset - это возможность за раз показывать несколько форм пользователю и соответственно затем проверять введенные данные и если нужно сохранять в БД. По аналогии с формами есть Formset и ModelFormset соответственно первый содержит набор из Form второй из ModelForm
Ну и разумеется всю подробную информацию можно найти в документации джанго:
Form, ModelForm, Formset, ModelFormset
По поводу DetailView в документации все подробно описано.
Тебе нужно создать DetailView, указать модель на основании которой оно будет строится. Затем тебе нужно в urls.py добавить конфигурацию урл, которое ведет на эту форму и в нем указать либо id, либо slug поле, по которому твою запись можно однозначно найти
По поводу того, как получить информацию о списке телефонов для конкретного пользователя.
Во первых я бы изначально во все ссылки на другие модели добавлял поле related_name
person_id = ForeignKey(Person, on_delete=models.CASCADE, related_name='phones')
и тогда что бы получить список телефонов по данному person, ты можешь использовать person.phones.all() или если, нужно использовать фильтры person.phones.filter(phone__startwith="+7").
Можно не добавлять related_name и django автоматически будет создавать modelname_set (phone_set в твоем случае), но если у тебя две ссылки на одну и ту же модель, то тут django будет бросать исключение, так как две обратные ссылки будут иметь одинаковое имя. Поэтому лучше сразу приучить себя использовать
related_name
Если вдруг кому пригодится хочу привести моё решение задачи. Огромное спасибо @AndreyMaslov за советы
как можно получить список телефонов Персоны с помощью DetailView
здесь мы используем модель Person и переопределяем метод get_context_data, добавляя в него все телефоны, которые относятся к конкретной персоне (я допустил ошибку при названии поля: person_id, надо быть просто написать person, потому как _id к названию Django само добавит. Из-за этого у меня получилось person_id_id)
urls.py
....
path('person/id=<int:pk>/', views.PersonDetail.as_view(), name='person_detail'),
....
views.py
class PersonDetail(DetailView):
model = Person
def get_context_data(self, **kwargs):
phones = PhoneNumber.objects.filter(person_id_id=self.kwargs.get('pk'))
context = super().get_context_data(**kwargs)
context["phones"] = phones
return context
person_detail.py
{% block content %}
<p>Name: {{person.name}}
<p>-=-=-=-=-=-=-=-=--==-=-=-==-=--=-==-=-=--==</p>
<p><u>Phones</u></p>
{% for phone in phones %}
<p> ................. {{phone}}</p>
{% endfor %}
{% endblock %}
И вариант с добавлением в модель поля related_name='' (добавляем его в модель Phone)
models.py
.....
class Phone(models.Model):
phone = models.CharField(max_length=10)
person_id = ForeignKey(Person, on_delete=models.CASCADE, related_name='phones')
и тогда можно сделать так:
views.py
class PersonDetail(DetailView):
model = Person
def get_context_data(self, **kwargs):
temp = Person.objects.get(pk=self.kwargs.get('pk'))
phones = temp.phones.all() # этот phones из модели Phone
context = super().get_context_data(**kwargs)
context["phones"] = phones
return context
шаблон остаётся тот же, что чуть ранее