web-dev-qa-db-ja.com

Django RESTフレームワークカスタムユーザーの作成

私はDjangoの領域では初めてですが、そこにはたくさんの「魔法」があることがわかります。 Django RESTフレームワークを使用して、無料のユーザー登録を可能にするアプリを作成しています。私のユーザーは、Djangoユーザーでは利用できないいくつかの追加フィールドを必要としています。だから私はユーザーを拡張するためにグーグルで検索しました。これはこのようなものを作成することによって行われるべきであるという考えがあります

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

これは問題ありませんが、私はこのシリアライザーを持っています

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

したがって、問題は、このシリアライザーがここで何らかの「魔法」を実行することです。モデルにどのフィールドが必要かを判断しようとします...ここにリストされているフィールドを持つユーザーが必要です。これらはユーザーにあり、「city」は新しいカスタムフィールドです。シリアライザーは、ユーザーモデルの内部を確認する必要があることを認識していません。

ここで何が欠けていますか?このシリアライザーに、ユーザー内にいくつかのフィールドが必要であることを伝えるにはどうすればよいですか?私もユーザーを作成できる必要があります。

20
Bojan Radojevic

そのため、回答の下にコメントを投稿するのに十分な評判がないようです。しかし、Kevin Stoneが説明したことを詳しく説明すると、モデルが次のようなものである場合:

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

このようなことを行うと、カスタムユーザーとDjango user:

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)
18
Anton K

さて、いくつかのこと。ユーザーモデル拡張用にOneToOneFieldを作成するとします。

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

さて、Django Rest Frameworkの力は、シリアライザーを構築して、シリアライズ時にこれらのモデルの両方からデータを取得できることです。

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

最後に、ユーザーを作成する場合、カスタムフィールドを使用しているため、入力データから両方のモデルを構築する独自のrestore_object()を実装する必要があります。

また、Djangoでユーザーを作成するのは少し異なります。create_user()を呼び出してハッシュされたパスワードを指定する必要があるため、シリアライザーのフィールドを保存するほど簡単ではありません。 。

13
Kevin Stone

このユースケースがドキュメントで見つけやすいとしたら、それは素晴らしいことです。 @jamodが指摘したように、DRF 3ではそれを見つけることができます ここ

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
Stuart

Django Rest Frameworkを使用する場合は、注意が必要です。カスタムユーザーモデルでは、組み込みのトークン認証を利用できません。それができるまでは、カスタムモデルのユーザーでOneToOneFieldを使用することをお勧めします。 。カスタムモデルには、保持する追加のフィールドが含まれます。1対1を使用すると、カスタムユーザーからユーザーにアクセスでき、ユーザーからカスタムユーザーにアクセスできます。

1
Zack Argyle

私はDjango signals モジュールを使用することを好みます。このモジュールは、何かが発生したときにアプリにシグナルを送信します。特に、前に独自の関数を呼び出すことができます/他の関数の後。私の答えはStuartの答えに似ていますが、新しい拡張クラスに関連するすべてのコードを1か所に保持します(後でプロファイルを削除したり、名前を変更したりする場合は、他の場所を探す必要はありません)。

次のコードは、拡張クラスモデル(この場合はユーザープロファイル)をレイアウトし、ユーザーモデルの作成時に空のインスタンスを作成し、親ユーザーインスタンスを保存することで、インスタンスを新しい情報(自分で追加する必要があります)で保存します。 - 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()

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

ユーザーを作成して保存すると、情報を追加するための空のuser.profileが作成されます。たとえば、python manage.py Shellを実行した後、次のことを試してください。

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
1
pale bone

Django 1.5以上を使用している場合は、代わりに カスタムユーザーモデル を使用します。この方法では、ユーザーモデルに専用のテーブルがあり、シリアライザーがフィールドを取得します。正しく。

1
mariodev