2009-09-27 14 views
18

Mam model o nazwie Student, który ma pewne pola i związek OneToOne z użytkownikiem (django.contrib.auth.User).Formularz modelu django. Uwzględnij pola z pokrewnych modeli

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) 

Następnie mam ModelForm dla tego modelu

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

Korzystanie pola atrybutów w klasie Meta, udało mi się pokazać tylko niektóre pola w szablonie. Czy mogę jednak wskazać, które pola użytkownika mają być wyświetlane?

Coś jak:

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

obecnie nie pokazuje nic. Działa tylko z StudentFields jednak/

Z góry dziękuję.

+0

jeśli modelu Student dziedziczone modelu użytkownika, który musiałby tylko jeden ModelForm. –

+0

@KevinL., Byłoby wspaniale, gdyby rozwinąć to w odpowiedzi :-) – cel

+0

@cel AFAIK, nie było takich wydarzeń, o których mowa w rdzeniu Django. "Automatyczne" rozwiązanie byłoby prawdopodobnie nietrywialne i wymagałoby napisania własnych niestandardowych klas modelu lub miksów, aby to zrobić. Byłoby to znacznie bardziej złożone (i prawdopodobnie bardziej kruche) niż przy użyciu sugerowanych metod. Jedno z możliwych rozwiązań może znaleźć się w [tej odpowiedzi] (https://stackoverflow.com/a/41559015/5747944), która opisuje mixin modelForm, który pozwala na zdefiniowanie drugiego modelu "dziecka" i jest uznawany za zgodny z widokami ogólnymi. – sytech

Odpowiedz

7

Obie odpowiedzi są poprawne: Inline Formsets uczynić ten sposób łatwe.

Należy jednak pamiętać, że wbudowany może działać tylko w jedną stronę: z modelu, który zawiera klucz obcy. Bez posiadania kluczy podstawowych w obu (złych, ponieważ możesz wtedy mieć A -> B, a następnie B -> A2), nie możesz mieć wbudowanego formularza w modelu related_to.

Na przykład, jeśli posiadasz klasę UserProfile i chcesz mieć je po pokazaniu, jeśli obiekt użytkownika jest powiązany, jak pokazano w linii, będziesz miał pecha.

Możesz mieć niestandardowe pola w ModelForm i używać tego jako bardziej elastycznego sposobu, ale należy pamiętać, że nie jest on już "automatyczny" jak standardowy modelForm/wbudowany zestaw formularzy.

+0

Uderzam dokładnie w problem opisany w tym pytaniu i twoją odpowiedź. Teraz minęło wiele lat. Czy wiesz, czy istnieje teraz automatyczne rozwiązanie? – cel

4

Powszechną praktyką jest zastosowanie 2 formularzy do osiągnięcia celu.

  • Formularz dla User Model:

    class UserForm(forms.ModelForm): 
        ... Do stuff if necessary ... 
        class Meta: 
         model = User 
         fields = ('the_fields', 'you_want') 
    
  • Formularz dla Student Model:

    class StudentForm (forms.ModelForm): 
        ... Do other stuff if necessary ... 
        class Meta: 
         model = Student 
         fields = ('the_fields', 'you_want') 
    
  • używać obu tych form w widoku (przykład użycia):

    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 formularze razem w szablonie:

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

Innym rozwiązaniem byłoby, aby zmienić relacje z OneToOne do ForeignKey (to całkowicie zależy od ciebie, a ja po prostu wspomnieć o tym, nie polecam tego) i użyj inline_formsets, aby osiągnąć pożądany rezultat.

+1

@cel Nie wiem o "automatycznym" sposobie (jak to określasz), ale mam rozwiązanie ręczne w tej odpowiedzi. Spójrz :) –

2

Alternatywną metodą, którą można rozważyć, jest stworzenie niestandardowego modelu użytkownika poprzez rozszerzenie modeli AbstractUser lub AbstractBaseUser zamiast korzystania z modelu one-to-one link z modelem profilu (w tym przypadku modelem Studenta). W ten sposób powstanie jeden rozszerzony model użytkownika, którego można użyć do utworzenia pojedynczego ModelForm.

Na przykład jednym ze sposobów, aby to zrobić byłoby rozszerzyć 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 

W settings.py pliku, zaktualizuj AUTH_USER_MODEL

AUTH_USER_MODEL = 'appname.models.Student' 

aktualizację modelu w swojej Admin:

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

admin.site.register(Student, UserAdmin) 

Następnie można użyć pojedynczego ModelForm, który ma oba dodatkowe pola yo Wymagaj tak samo jak pól w oryginalnym modelu użytkownika. W forms.py

from .models import Student  

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

bardziej skomplikowany sposób byłoby rozszerzyć AbstractBaseUser to opisano szczegółowo in the docs.

Jednak nie jestem pewien, czy tworzenie niestandardowego modelu użytkownika w ten sposób, aby mieć wygodny pojedynczy model Model, ma sens w przypadku użycia. Jest to decyzja projektowa, którą musisz podjąć, ponieważ tworzenie niestandardowych modeli użytkowników może być trudnym ćwiczeniem.

1

Jeśli nie chcesz zmieniać AUTH_USER_MODEL, który ma wiele skutków ubocznych, można użyć Multi-table inheritance i podklasy modelu użytkownika zamiast AbstractUser. Spowoduje to utworzenie Student stolik z OneToOneField nazwie user_ptr, który wskazuje na użytkownika tabeli.

Oto przykład

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

Teraz można zdefiniować ModelForm jak ten

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

Możesz również rozszerzyć wbudowaną w Django formularzy użytkownika, takie jak ten

from django.contrib.auth.forms import UserChangeForm 

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

Aby użyć formularza w administratorze django, dodaj t on następujące do 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) 

subkomitet uznającej użytkownika modelu, tworząc Student instancję automatycznie utworzy nową instancję użytkownika, ale nie na odwrót. Tak więc instancja może istnieć bez powiązania z instancją Student.Jeśli chcesz mieć pewność, że instancja Student jest tworzony dla każdego Użytkownika w systemie, można użyć następujący sygnał:

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

Z mojego zrozumienia chcesz zaktualizować polu Nazwa użytkownika, który jest auth.User OneToOne relacja z Student, to co bym zrobił ...

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) 

nadzieję, że to pomoże :)

Powiązane problemy