2013-11-04 9 views
17

Jestem nowy w świecie Django, ale widzę tam "magię". Używam Django REST Framework i tworzę aplikację, która pozwoli na bezpłatną rejestrację użytkownika. Mój użytkownik potrzebuje dodatkowych pól, które nie są dostępne w Django. Dlatego szukałem w Google rozszerzenia na użytkownika. Istnieje pogląd, że należy to zrobić poprzez stworzenie coś takiegoDjango REST Framework Tworzenie niestandardowego użytkownika

class MyUser(models.Model): 
    user = models.ForeignKey(User, unique=True) 
    city = models.CharField(max_length=50, blank=True, default='') 

to jest w porządku, ale mam ten serializer

class UserSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = MyUser 
     fields = ('id', 'username', 'password', 'first_name', 'last_name', 'email', 'city') 

Więc problemem jest to, że serializer robi jakieś „magiczne” tutaj . Próbuje dowiedzieć się, jakie pole powinien mieć model ... Chcę mieć użytkownika z polami wymienionymi tutaj, a te pola są w User, a "city" to nowe pole niestandardowe. Serializator nie zrozumiałby, że powinien wyglądać wewnątrz modelu użytkownika.

Czego mi tu brakuje? Jak powiedzieć temu serializatorowi, że chcę niektóre pola wewnątrz użytkownika? Muszę też móc kreować użytkownika.

+6

Należy użyć 'OneToOneField' dla stosunku UserModel, a nie' ForeignKey' od ich może być tylko jeden '' MyUser' dla każdej instancji użytkownik portu. –

+0

Użyj 'OneToOneField' i postępuj zgodnie z tą odpowiedzią i dobrze Ci idzie: [link] (http://stackoverflow.com/a/28733782/3294412) – musicformellons

Odpowiedz

1

Jeśli używasz django 1.5 lub nowszego, użyj zamiast tego custom user model, w ten sposób model użytkownika będzie posiadał własną dedykowaną tabelę, a serializator będzie następnie poprawnie zbierać pola.

+0

Pomyśl, że to naprawdę nie jest opcja, prawdopodobnie będę używać 1.4 . Naprawdę nie rozumiem, czy to naprawdę trudne? Dlaczego? I szwy jak standardowe rzeczy do zrobienia, dlaczego jest to tak skomplikowane? – bradojevic

1

Podczas korzystania z Django Rest Framework musisz zachować ostrożność. Każdy niestandardowy model użytkownika nie może korzystać z wbudowanego uwierzytelniania tokenu. Dopóki tego nie zrobisz, proponuję użyć OneToOneField z użytkownikiem w twoim modelu niestandardowym. Twój niestandardowy model będzie zawierał dodatkowe pola, które chcesz zachować. Funkcja One to One zapewnia użytkownikowi dostęp od użytkownika niestandardowego i niestandardowego użytkownika od użytkownika.

+5

Minął prawie rok, odkąd to opublikowałeś - czy DRF obsługuje niestandardowe modele użytkowników Django, czy ta odpowiedź jest nadal poprawna, a OneToOneFields z użytkownikami to wciąż najlepszy pomysł? – ArtOfWarfare

12

Okay, kilka rzeczy. Chcesz utworzyć OneToOneField dla swojego rozszerzenia modelu użytkownika.

class MyUser(models.Model): 
    user = models.OneToOneField(User) 
    city = models.CharField(max_length=50, blank=True, default='') 

Teraz, moc Django Rest Framework, jest można zbudować serializatora, aby pobierać dane z obu tych modeli podczas szeregowania.

class UserSerializer(serializers.ModelSerializer): 
    city = serializers.CharField(source='myuser.city') 
    class Meta: 
     model = User 
     fields = ('id', 'username', 'password', 'first_name', 'last_name', 'email', 'city') 

Wreszcie mamy swoje stworzenie użytkownikowi, ponieważ używasz pól niestandardowych, trzeba zaimplementować własną restore_object() że buduje oba modele od danych wejściowych.

Również tworzenie użytkowników w Django jest nieco inne, musisz zadzwonić pod numer create_user() i podać hasło, które jest hashowane, więc nie jest tak proste jak przechowywanie pól z serializera.

+0

Czy możesz rozwinąć swoją odpowiedź, aby dołączyć przykładową implementację 'restore_object()' i 'create_user()'? Nie jestem pewien, jak powinienem napisać hasło ... (fakt, że sugerujesz nawet, że musimy to zrobić, budzi w mojej głowie czerwone flagi - czy Django, ani DRF nie powinny dostarczać tego rodzaju zabezpieczeń z pudełko?) – ArtOfWarfare

+0

Dziękuję za tę odpowiedź! Dodając do niego, możesz również przejść do atrybutu id "user_profile = serializers.CharField (source = 'userprofile.id')'. – pasql

16

Najwyraźniej nie mam wystarczającej reputacji, aby opublikować komentarz pod odpowiedzią. Ale żeby opracować na co opisano Kevin Kamień, jeśli model jest coś takiego, co następuje:

class AppUser(models.Model): 
    user = models.OneToOneField(User) 
    ban_status = models.BooleanField(default=False) 

można zrobić coś takiego stworzyć zarówno na zlecenie użytkownika i django użytkownika:

class AppUserSerializer(serializers.ModelSerializer): 
    username = serializers.CharField(source='user.username') 
    email = serializers.CharField(source='user.email') 
    password = serializers.CharField(source='user.password') 
    ban_status = serializers.Field(source='ban_status') 

    class Meta: 
     model = AppUser 
     fields = ('id', 'username', 'email', 'password', 'ban_status') 

    def restore_object(self, attrs, instance=None): 
     """ 
     Given a dictionary of deserialized field values, either update 
     an existing model instance, or create a new model instance. 
     """ 
     if instance is not None: 
      instance.user.email = attrs.get('user.email', instance.user.email) 
      instance.ban_status = attrs.get('ban_status', instance.ban_status) 
      instance.user.password = attrs.get('user.password', instance.user.password) 
      return instance 

     user = User.objects.create_user(username=attrs.get('user.username'), email= attrs.get('user.email'), password=attrs.get('user.password')) 
     return AppUser(user=user) 
+4

Należy zauważyć, że funkcja restore_object() nie jest zgodna z DRF 3.x, więc należy zamiast tego użyć metod create() i update(). Przykład: http://www.django-rest-framework.org/topics/3.0-announcement/#serializers. – jarmod

0

Byłoby bądź miły, jeśli ten przypadek użycia był łatwiejszy do znalezienia w dokumentach. Jak @jamod wskazał w DRF 3, można go znaleźć here:

class UserSerializer(serializers.ModelSerializer): 
    profile = ProfileSerializer() 

    class Meta: 
     model = User 
     fields = ('username', 'email', 'profile') 

    def create(self, validated_data): 
     profile_data = validated_data.pop('profile') 
     user = User.objects.create(**validated_data) 
     Profile.objects.create(user=user, **profile_data) 
     return user 
1

wolę używać moduł django signals, który wysyła sygnały do ​​aplikacji, gdy coś się dzieje, a między innymi pozwoli Ci zadzwonić funkcja twoich własnych przed/po innych funkcjach.Moja odpowiedź jest podobna do odpowiedzi Stuarta, ale zachowuje cały kod związany z twoją nową klasą rozszerzeń w jednym miejscu (jeśli chcesz później usunąć profil lub zmienić jego nazwę, nie musisz szukać nigdzie indziej).

Poniższy kod określa rozszerzony model klasy, w tym przypadku profil użytkownika, następnie tworzy puste wystąpienie po utworzeniu modelu użytkownika, a następnie zapisuje instancję z nowymi informacjami (które należy dodać), zapisując rodzic instancja użytkownik IE - user.save()

models.py

from django.db.models.signals import post_save 
from django.db import models 
from django.contrib.auth.models import User 

class Profile(models.Model): #This extends the user class to add profile information 
    user = models.OneToOneField(User, on_delete=models.CASCADE) 
    #add your extra traits here: is_nice, is_shwifty, address, etc. 
    is_nice = models.BooleanField(default=True, blank=True) 

# a user model was just created! This now creates your extended user (a profile): 
@receiver(post_save, sender=User) 
def create_user_profile(sender, instance, created, **kwargs): 
    if created: 
     # instance is the user model being saved. 
     Profile.objects.create(user=instance) 

# a user model was just saved! This now saves your extended user (a profile): 
@receiver(post_save, sender=User) 
def save_user_profile(sender, instance, **kwargs): 
     instance.profile.save() 

Jeśli nie masz ProfileSerializer: serializers.py

#use hyperlinkedmodelserializer for easy api browsing + clicking 
class ProfileSerializer(serializers.HyperlinkedModelSerializer): 
    user = UserSerializer() 
    class Meta: 
     model = Profile 
     fields = ('url', 'user', 'is_nice') 

Po utworzeniu użytkownika i zapisaniu użytkownika otrzymasz pusty user.profile w celu dodania informacji. Na przykład, po uruchomieniu python manage.py shell spróbować:

from backend.models import User, Profile 
#create your user 
user=User(username="GravyBoat") 
user.save() 
#now update your user's profile 
user.profile.is_nice=False 
#that's one mean gravy boat 
user.save() 
user.profile.is_nice 
#False