2009-09-27 44 views
18

Tôi có một mô hình, được gọi là Sinh viên, có một số trường và mối quan hệ OneToOne với người dùng (django.contrib.auth.User).Mẫu mô hình django. Bao gồm các trường từ các mô hình liên quan

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) 

Sau đó, tôi có một ModelForm cho rằng mô hình

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

Sử dụng thuộc tính các trường trong lớp Meta, tôi đã quản lý để chỉ hiển thị một số lĩnh vực trong một mẫu. Tuy nhiên, tôi có thể cho biết những trường người dùng nào sẽ hiển thị?

Something như:

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

hiện không hiển thị bất cứ điều gì. Chỉ hoạt động với StudentFields mặc dù/

Cảm ơn bạn trước.

+0

nếu mô hình sinh kế thừa mô hình tài khoản bạn sẽ chỉ cần một ModelForm. –

+0

@KevinL., Nó sẽ là tuyệt vời nếu bạn xây dựng điều này trong một câu trả lời :-) – cel

+0

@cel AFAIK, đã có không có sự phát triển như thế này để nói về trong lõi Django. Một giải pháp 'tự động' ở đây có thể sẽ không nhỏ và liên quan đến việc viết các lớp mô hình tùy chỉnh của riêng bạn hoặc các mixin để làm như vậy. Điều này sẽ phức tạp hơn nhiều (và cho là dễ vỡ hơn) so với sử dụng các phương pháp được đề xuất. Một giải pháp có thể có trong [câu trả lời] này (https://stackoverflow.com/a/41559015/5747944) mô tả một mixin ModelForm cho phép xác định mô hình 'con' thứ hai và được tuyên bố là tương thích với các khung nhìn chung. – sytech

Trả lời

7

Cả hai câu trả lời là chính xác: Inline Formsets làm làm điều này dễ dàng.

Lưu ý rằng, nội tuyến chỉ có thể đi một chiều: từ mô hình có khóa ngoại trong đó. Không có khóa chính trong cả hai (xấu, vì sau đó bạn có thể có A -> B và sau đó B -> A2), bạn không thể có formset nội tuyến trong mô hình related_to.

Ví dụ, nếu bạn có một lớp UserProfile, và muốn có thể có, khi được hiển thị, có đối tượng người dùng có liên quan được hiển thị như trong nội tuyến, bạn sẽ không may mắn.

Bạn có thể có các trường tùy chỉnh trên ModelForm và sử dụng trường này theo cách linh hoạt hơn, nhưng lưu ý rằng nó không còn 'tự động' như một bộ mẫu ModelForm/inline chuẩn.

+0

Tôi đang nhấn chính xác vấn đề được mô tả trong câu hỏi này và câu trả lời của bạn. Bây giờ nhiều năm trôi qua. Bạn có biết liệu có một giải pháp tự động ngay bây giờ không? – cel

4

Thông lệ chung là sử dụng 2 biểu mẫu để đạt được mục tiêu của bạn.

  • Một hình thức cho User mẫu:

    class UserForm(forms.ModelForm): 
        ... Do stuff if necessary ... 
        class Meta: 
         model = User 
         fields = ('the_fields', 'you_want') 
    
  • Một hình thức cho Student mẫu:

    class StudentForm (forms.ModelForm): 
        ... Do other stuff if necessary ... 
        class Meta: 
         model = Student 
         fields = ('the_fields', 'you_want') 
    
  • Sử dụng cả những hình thức theo quan điểm của bạn (ví dụ về sử dụng):

    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 các hình thức với nhau trong mẫu của bạn:

    <form action="." method="post"> 
        {% csrf_token %} 
        {{ user_form.as_p }} 
        {{ student_form.as_p }} 
        <input type="submit" value="Submit"> 
    </form> 
    

Một lựa chọn khác sẽ cho bạn thay đổi mối quan hệ OneToOne-ForeignKey (điều này hoàn toàn phụ thuộc vào bạn và tôi chỉ đề cập đến nó, không khuyến khích nó) và sử dụng inline_formsets để đạt được kết quả mong muốn.

+1

@cel Tôi không biết về một cách "tự động" (như bạn đặt nó) nhưng tôi có một giải pháp thủ công trong câu trả lời này. Có một cái nhìn :) –

2

Một phương pháp thay thế mà bạn có thể xem xét là tạo mô hình người dùng tùy chỉnh bằng cách mở rộng mô hình AbstractUser hoặc AbstractBaseUser thay vì sử dụng one-to-one link với mô hình Hồ sơ (trong trường hợp này là mô hình Sinh viên). Điều này sẽ tạo ra một mô hình User mở rộng duy nhất mà bạn có thể sử dụng để tạo ra một ModelForm duy nhất.

Ví dụ một cách để làm điều này sẽ được mở rộng 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 

Trong file settings.py, cập nhật AUTH_USER_MODEL

AUTH_USER_MODEL = 'appname.models.Student' 

cập nhật mô hình trong Admin của bạn:

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

admin.site.register(Student, UserAdmin) 

Sau đó, bạn có thể sử dụng một ModelForm duy nhất có cả hai trường bổ sung yo u yêu cầu cũng như các trường trong mô hình Người dùng ban đầu. Trong forms.py

from .models import Student  

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

Một cách phức tạp hơn sẽ được mở rộng AbstractBaseUser, điều này được mô tả chi tiết in the docs.

Tuy nhiên, tôi không chắc liệu tạo mô hình người dùng tùy chỉnh theo cách này để có một ModelForm đơn tiện lợi có ý nghĩa đối với trường hợp sử dụng của bạn hay không. Đây là một quyết định thiết kế mà bạn phải thực hiện kể từ khi tạo mô hình Người dùng tùy chỉnh có thể là một bài tập khó khăn.

1

Nếu bạn không muốn thay đổi AUTH_USER_MODEL trong đó có nhiều tác dụng phụ, bạn có thể sử dụng Multi-table inheritance và phân lớp các mô hình tài thay vì AbstractUser. Điều này sẽ tạo ra một Student bảng với một OneToOneField tên user_ptr trỏ đến tài bảng.

Dưới đây là một ví dụ

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') 

Bây giờ bạn có thể định nghĩa bạn ModelForm như thế này

class StudentForm(forms.ModelForm): 
    class Meta: 
     model = Student 
     fields = ('first_name', 'last_name', 'username', 
        'personalInfo', 'phone', 'birthdate', 'city') 

Bạn cũng có thể mở rộng được xây dựng theo các hình thức sử dụng django như thế này

from django.contrib.auth.forms import UserChangeForm 

class StudentForm(UserChangeForm): 
    class Meta: 
     model = Student 
     fields = ('first_name', 'last_name', 'username', 
       'personalInfo', 'phone', 'birthdate', 'city') 

Để sử dụng biểu mẫu của bạn trong quản trị django, hãy thêm t ông sau để 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) 

theo tiểu classing các tài mô hình, tạo ra một sinh viên dụ sẽ tự động tạo một đối tượng người dùng mới nhưng không phải là cách khác xung quanh. Vì vậy, một cá thể Người dùng có thể tồn tại mà không được liên kết với cá thể Sinh viên.Nếu bạn muốn đảm bảo rằng một trường hợp sinh viên được tạo ra cho mỗi tài trong hệ thống, bạn có thể sử dụng các tín hiệu sau:

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

Từ hiểu biết của tôi bạn muốn cập nhật các lĩnh vực tên người dùng của auth.User đó là OneToOne mối quan hệ với Student, đây là những gì tôi sẽ làm ...

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) 

hy vọng điều này giúp :)

Các vấn đề liên quan