2009-09-27 194 views
18

我有一個名爲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合作,但是/

在此先感謝。

+0

如果學生模型繼承了用戶模型,你只需要一個的ModelForm。 – 2018-01-26 10:27:19

+0

@KevinL。,如果你在一個答案中詳細說明這一點,那將是非常棒的:-) – cel 2018-01-27 07:41:21

+0

@cel AFAIK,在Django核心中沒有像這樣的發展。這裏的'自動'解決方案可能不是微不足道的,並且涉及編寫自己的自定義模型表單類或混合類來執行此操作。這比使用建議的方法要複雜得多(可以說更脆弱)。一個可能的解決方案可能包含在[這個回答](https://stackoverflow.com/a/41559015/5747944)中,該解決方案描述了一個ModelForm mixin,它允許定義第二個「子」模型並聲稱與通用視圖兼容。 – sytech 2018-01-31 14:20:33

回答

7

這兩個答案都是正確的:內聯表單集使這樣做很容易。

但是,請注意,內聯只能走一條路:從具有外鍵的模型開始。在兩個主鍵都沒有的情況下(壞的,因爲你可能有A→B然後B→A2),你不能在related_to模型中有內聯表單。

例如,如果您有一個UserProfile類,並且希望能夠在顯示時擁有這些類,那麼將會顯示相關的User對象,如果是inline,那麼您的運氣就不好。

您可以在ModelForm上擁有自定義字段,並將其用作更靈活的方式,但請注意,它不再像標準ModelForm /內聯formset那樣是「自動」的。

+0

我碰到這個問題和你的答案中描述的問題。現在已經過去了很多年。你知道現在是否有自動解決方案嗎? – cel 2018-01-26 07:06:00

4

一個常見的做法是使用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> 
    

另一種選擇是你改變從OneToOneForeignKey的關係(這完全取決於你,我只是提到它,不推薦它),並使用inline_formsets來達到預期的效果。

+1

@cel我不知道「自動」的方式(就像你說的那樣),但我在這個答案中有一個手動的解決方案。看一看 :) – 2018-01-26 09:49:24

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對您的用例有意義。由於創建自定義用戶模型是一項棘手的工作,因此這是您必須進行的設計決策。

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中使用你的表單,添加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() 
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) 

希望這有助於:)