django模型表。包括相关模型的字段

时间:2009-09-27 14:12:13

标签: python django django-forms

我有一个名为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属性,我设法只显示模板中的一些字段。但是,我可以指出要显示的用户字段吗?

东西:

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

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

提前致谢。

5 个答案:

答案 0 :(得分:11)

通常的做法是使用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来达到预期的效果。

答案 1 :(得分:7)

两个答案都是正确的:内联表单集可以轻松完成。

但请注意,内联只能采用单向方式:从具有外键的模型中进行。如果两者中都没有主键(不好,那么你可以使用A - > B,然后是B - > A2),则不能在related_to模型中使用内联formset。

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

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

答案 2 :(得分:2)

您可以考虑的另一种方法是通过扩展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。这是您必须做出的设计决定,因为创建自定义用户模型可能是一项棘手的工作。

答案 3 :(得分:1)

如果您不想更改有很多副作用的 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中使用您的表单,请将以下内容添加到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()

答案 4 :(得分:1)

根据我的理解,你想更新auth.User的用户名字段OneToOneStudent的关系,这就是我要做的......

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)

希望这有助于:)