Форма модели django. Включить поля из связанных моделей
У меня есть модель, называемая Student, у которой есть некоторые поля, и отношения OneToOne с пользователем (django.contrib.auth.User).
class Student(models.Model):
phone = models.CharField(max_length = 25 )
birthdate = models.DateField(null=True)
gender = models.CharField(max_length=1,choices = GENDER_CHOICES)
city = models.CharField(max_length = 50)
personalInfo = models.TextField()
user = models.OneToOneField(User,unique=True)
Затем у меня есть ModelForm для этой модели
class StudentForm (forms.ModelForm):
class Meta:
model = Student
Используя атрибут полей в классе Meta, мне удалось отобразить только некоторые поля в шаблоне. Однако могу ли я указать, какие поля пользователя показывать?
Что-то вроде:
fields =('personalInfo','user.username')
в настоящее время ничего не показывает. Работает только с StudentFields, хотя /
Спасибо заранее.
Ответы
Ответ 1
Оба ответа верны: встроенные Formsets делают это легко.
Помните, однако, что встроенная строка может идти только в одну сторону: от модели, в которой есть внешний ключ. Без наличия первичных ключей в обоих (плохо, поскольку вы могли бы тогда иметь A → B, а затем B → A2), вы не можете иметь встроенный набор форм в связанной модели.
Например, если у вас есть класс UserProfile и вы хотите иметь их, когда показано, есть объект User, который связан, как показано на рисунке, вам не повезло.
У вас могут быть настраиваемые поля в ModelForm и использовать это как более гибкий способ, но имейте в виду, что он больше не является "автоматическим", как стандартный набор форм ModelForm/inline.
Ответ 2
Общей практикой является использование двух форм для достижения вашей цели.
-
Форма для User
Модель:
class UserForm(forms.ModelForm):
... Do stuff if necessary ...
class Meta:
model = User
fields = ('the_fields', 'you_want')
-
Форма для Student
Модель:
class StudentForm (forms.ModelForm):
... Do other stuff if necessary ...
class Meta:
model = Student
fields = ('the_fields', 'you_want')
-
Используйте обе формы в своем представлении (пример использования):
def register(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
student_form = StudentForm(request.POST)
if user_form.is_valid() and student_form.is_valid():
user_form.save()
student_form.save()
-
Вставьте формы вместе в свой шаблон:
<form action="." method="post">
{% csrf_token %}
{{ user_form.as_p }}
{{ student_form.as_p }}
<input type="submit" value="Submit">
</form>
Другим вариантом было бы изменить отношение от OneToOne
до ForeignKey
(это полностью зависит от вас, и я просто упомянул об этом, не рекомендую его) и используйте inline_formsets
для достижения желаемого результата.
Ответ 3
Альтернативным методом, который можно было бы рассмотреть, является создание пользовательской модели путем расширения моделей AbstractUser или AbstractBaseUser вместо использования ссылки "один-к-одному" с помощью Модель профиля (в данном случае модель Студента). Это создало бы одну расширенную модель пользователя, которую можно использовать для создания единственного ModelForm.
Например, одним из способов сделать это будет расширение AbstractUser model:
from django.contrib.auth.models import AbstractUser
class Student(AbstractUser):
phone = models.CharField(max_length = 25 )
birthdate = models.DateField(null=True)
gender = models.CharField(max_length=1,choices = GENDER_CHOICES)
city = models.CharField(max_length = 50)
personalInfo = models.TextField()
# user = models.OneToOneField(User,unique=True) <= no longer required
В файле settings.py обновите AUTH_USER_MODEL
AUTH_USER_MODEL = 'appname.models.Student'
обновите модель в своем Admin:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import Student
admin.site.register(Student, UserAdmin)
Затем вы можете использовать один ModelForm, который имеет как дополнительные поля, которые вам нужны, так и поля в исходной модели пользователя. В forms.py
from .models import Student
class StudentForm (forms.ModelForm):
class Meta:
model = Student
fields = ['personalInfo', 'username']
Более сложным способом было бы расширить AbstractBaseUser, это подробно описано в документах.
Тем не менее, я не уверен, создает ли пользовательскую пользовательскую модель таким образом, чтобы иметь удобную единую модель ModelForm для вашего случая использования. Это дизайнерское решение, которое вы должны сделать, поскольку создание пользовательских моделей пользователей может быть сложным упражнением.
Ответ 4
Если вы не хотите менять AUTH_USER_MODEL, у которого много побочных эффектов, вы можете использовать Наследование на несколько таблиц и подкласс модели Пользователь вместо AbstractUser. Это создаст таблицу Студент с OneToOneField с именем user_ptr, которая указывает на таблицу Пользователь.
Здесь пример
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _
class Student(User):
phone = models.CharField(max_length=25)
birthdate = models.DateField(null=True)
city = models.CharField(max_length=50)
personalInfo = models.TextField()
class Meta:
verbose_name = _('student')
verbose_name_plural = _('students')
Теперь вы можете определить ModelForm как
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ('first_name', 'last_name', 'username',
'personalInfo', 'phone', 'birthdate', 'city')
Вы также можете расширить встроенные пользовательские формы django, такие как
from django.contrib.auth.forms import UserChangeForm
class StudentForm(UserChangeForm):
class Meta:
model = Student
fields = ('first_name', 'last_name', 'username',
'personalInfo', 'phone', 'birthdate', 'city')
Чтобы использовать вашу форму в администраторе django, добавьте следующее в admin.py:
from django.contrib import admin
from .views import StudentForm
from .models import Student
class StudentAdmin(admin.ModelAdmin):
form = StudentForm
admin.site.register(Student, StudentAdmin)
Посредством класса Пользователь создание экземпляра Студент автоматически создаст новый экземпляр пользователя, но не наоборот. Таким образом, экземпляр Пользователь может существовать без связи с экземпляром Студент. Если вы хотите убедиться, что экземпляр Student создан для каждого Пользователь в системе, вы можете использовать следующий сигнал:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Student
@receiver(post_save, sender=User)
def create_student(sender, instance, created, **kwargs):
if created:
student = Student(user_ptr_id=instance.pk)
student.__dict__.update(instance.__dict__)
student.save()
Ответ 5
Из моего понимания вы хотите обновить поле имени пользователя auth.User, которое имеет отношение OneToOne
с Student
, это то, что я бы сделал...
class StudentForm (forms.ModelForm):
username = forms.Charfield(label=_('Username'))
class Meta:
model = Student
fields = ('personalInfo',)
def clean_username(self):
# using clean method change the value
# you can put your logic here to update auth.User
username = self.cleaned_data('username')
# get AUTH USER MODEL
in_db = get_user_model()._default_manager.update_or_create(username=username)
надеюсь, что это поможет:)