python django 模型表单。包括来自相关模型的字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1483647/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:22:58  来源:igfitidea点击:

django model Form. Include fields from related models

pythondjangodjango-forms

提问by Tom

I have a model, called Student, which has some fields, and a OneToOne relationship with a user (django.contrib.auth.User).

我有一个名为 Student 的模型,它有一些字段,以及与用户 (django.contrib.auth.User) 的 OneToOne 关系。

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)

Then, I have a ModelForm for that model

然后,我有该模型的 ModelForm

class StudentForm (forms.ModelForm):
    class Meta:
        model = Student

Using the fields attribute in class Meta, I've managed to show only some fields in a template. However, can I indicate which user fields to show?

使用类 Meta 中的 fields 属性,我设法仅显示模板中的某些字段。但是,我可以指出要显示哪些用户字段吗?

Something as:

一些东西:

   fields =('personalInfo','user.username')

is currently not showing anything. Works with only StudentFields though/

目前没有显示任何内容。虽然仅适用于 StudentFields/

Thanks in advance.

提前致谢。

回答by John Moutafis

A common practice is to use 2 forms to achieve your goal.

一种常见的做法是使用 2 种形式来实现您的目标。

  • A form for the UserModel:

    class UserForm(forms.ModelForm):
        ... Do stuff if necessary ...
        class Meta:
            model = User
            fields = ('the_fields', 'you_want')
    
  • A form for the StudentModel:

    class StudentForm (forms.ModelForm):
        ... Do other stuff if necessary ...
        class Meta:
            model = Student
            fields = ('the_fields', 'you_want')
    
  • Use both those forms in your view (example of usage):

    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()
    
  • Render the forms together in your template:

    <form action="." method="post">
        {% csrf_token %}
        {{ user_form.as_p }}
        {{ student_form.as_p }}
        <input type="submit" value="Submit">
    </form>
    
  • 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>
    


Another option would be for you to change the relationship from OneToOneto ForeignKey(this completely depends on you and I just mention it, not recommend it) and use the inline_formsetsto achieve the desired outcome.

另一种选择是让您将关系从 更改OneToOneForeignKey(这完全取决于您,我只是提到它,而不是推荐它)并使用inline_formsets来实现所需的结果。

回答by Matthew Schinckel

Both answers are correct: Inline Formsets make doing this easy.

两个答案都是正确的:内联表单集使这件事变得容易。

Be aware, however, that the inline can only go one way: from the model that has the foreign key in it. Without having primary keys in both (bad, since you could then have A -> B and then B -> A2), you cannot have the inline formset in the related_to model.

但是请注意,内联只能通过一种方式:从其中包含外键的模型。如果两者都没有主键(不好,因为你可以有 A -> B 然后 B -> A2),你就不能在 related_to 模型中拥有内联表单集。

For instance, if you have a UserProfile class, and want to be able to have these, when shown, have the User object that is related shown as in inline, you will be out of luck.

例如,如果您有一个 UserProfile 类,并且希望能够拥有这些,当显示时,将相关的 User 对象显示为内联,那么您将不走运。

You can have custom fields on a ModelForm, and use this as a more flexible way, but be aware that it is no longer 'automatic' like a standard ModelForm/inline formset.

您可以在 ModelForm 上拥有自定义字段,并将其用作更灵活的方式,但请注意,它不再像标准 ModelForm/内联表单集那样“自动”。

回答by Kevin L.

An alternative method that you could consider is to create a custom user model by extending the AbstractUser or AbstractBaseUser models rather than using a one-to-one linkwith a Profile model (in this case the Student model). This would create a single extended User model that you can use to create a single ModelForm.

您可以考虑的另一种方法是通过扩展 AbstractUser 或 AbstractBaseUser 模型来创建自定义用户模型,而不是使用与 Profile 模型(在本例中为 Student 模型)的一对一链接。这将创建一个可用于创建单个 ModelForm 的扩展 User 模型。

For instance one way to do this would be to extend the AbstractUser model:

例如,一种方法是扩展AbstractUser 模型

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

In settings.py file, update the AUTH_USER_MODEL

在 settings.py 文件中,更新 AUTH_USER_MODEL

AUTH_USER_MODEL = 'appname.models.Student'

update the model in your Admin:

在您的管理员中更新模型:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import Student

admin.site.register(Student, UserAdmin)

Then you can use a single ModelForm that has both the additional fields you require as well as the fields in the original User model. In forms.py

然后,您可以使用单个 ModelForm,其中包含您需要的附加字段以及原始 User 模型中的字段。在forms.py中

from .models import Student    

class StudentForm (forms.ModelForm):
    class Meta:
        model = Student
        fields = ['personalInfo', 'username']

A more complicated way would be to extend the AbstractBaseUser, this is described in detail in the docs.

更复杂的方法是扩展 AbstractBaseUser,这在 docs中有详细描述。

However, I'm not sure whether creating a custom user model this way in order to have a convenient single ModelForm makes sense for your use case. This is a design decision you have to make since creating custom User models can be a tricky exercise.

但是,我不确定以这种方式创建自定义用户模型以获得方便的单个 ModelForm 是否对您的用例有意义。这是您必须做出的设计决策,因为创建自定义用户模型可能是一项棘手的练习。

回答by Sanchit

From my understanding you want to update the username field of auth.User which is OneToOnerelation with Student, this is what I would do...

从我的理解要更新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)

hope this helps :)

希望这可以帮助 :)