django模型表单。包含来自相关模型的字段
我有一个名为Student的模型,其中包含一些字段以及与用户的OneToOne关系(django.contrib.auth.User)。django模型表单。包含来自相关模型的字段
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
使用领域类元属性,我已经成功地只显示在模板中的一些领域。但是,我可以指出要显示哪些用户字段?
东西为:
fields =('personalInfo','user.username')
目前未显示任何。尽管只与StudentFields合作,但是/
在此先感谢。
这两个答案都是正确的:内联表单集使这样做很容易。
但是,请注意,内联只能走一条路:从具有外键的模型开始。在两个主键都没有的情况下(坏的,因为你可能有A→B然后B→A2),你不能在related_to模型中有内联表单。
例如,如果您有一个UserProfile类,并且希望能够在显示时拥有这些类,那么将会显示相关的User对象,如果是inline,那么您的运气就不好。
您可以在ModelForm上拥有自定义字段,并将其用作更灵活的方式,但请注意,它不再像标准ModelForm /内联formset那样是“自动”的。
我碰到这个问题和你的答案中描述的问题。现在已经过去了很多年。你知道现在是否有自动解决方案吗? – cel 2018-01-26 07:06:00
一个常见的做法是使用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
来达到预期的效果。
@cel我不知道“自动”的方式(就像你说的那样),但我在这个答案中有一个手动的解决方案。看一看 :) – 2018-01-26 09:49:24
您可以考虑的另一种方法是通过扩展AbstractUser或AbstractBaseUser模型而不是使用具有Profile模型的one-to-one link(本例中为Student模型)来创建自定义用户模型。这将创建一个可用于创建单个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'
更新在您的管理模式:
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,对此进行详细描述in the docs。
但是,我不确定是否以这种方式创建自定义用户模型以获得方便的单个ModelForm对您的用例有意义。由于创建自定义用户模型是一项棘手的工作,因此这是您必须进行的设计决策。
如果你不想改变AUTH_USER_MODEL它有许多副作用,您可以使用Multi-table inheritance和子类用户模型代替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中使用你的表单,添加t他对以下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)
按分分级的用户模型,创建学生情况下会自动创建一个新的用户实例,但不是周围的其他方式。因此,一个实例可以存在而不与一个学生实例相关联。如果你想确保学生实例系统中的每一个用户创建,您可以使用以下信号:
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()
从我的理解要更新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)
希望这有助于:)
如果学生模型继承了用户模型,你只需要一个的ModelForm。 – 2018-01-26 10:27:19
@KevinL。,如果你在一个答案中详细说明这一点,那将是非常棒的:-) – cel 2018-01-27 07:41:21
@cel AFAIK,在Django核心中没有像这样的发展。这里的'自动'解决方案可能不是微不足道的,并且涉及编写自己的自定义模型表单类或混合类来执行此操作。这比使用建议的方法要复杂得多(可以说更脆弱)。一个可能的解决方案可能包含在[这个回答](https://stackoverflow.com/a/41559015/5747944)中,该解决方案描述了一个ModelForm mixin,它允许定义第二个“子”模型并声称与通用视图兼容。 – sytech 2018-01-31 14:20:33