Django Rest Framework делает OnetoOne отношением корабля, похоже, что это одна модель
У меня есть мой User
, сохраненный в двух разных моделях, UserProfile
и User
. Теперь, с точки зрения API, никто не заботится о том, что эти две разные.
Итак, у меня есть:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'first_name', 'last_name', 'email')
и
class UserPSerializer(serializers.HyperlinkedModelSerializer):
full_name = Field(source='full_name')
class Meta:
model = UserProfile
fields = ('url', 'mobile', 'user','favourite_locations')
Итак, в UserPSerializer
поле User
является просто ссылкой на этот ресурс. Но сформировать пользовательскую перспективу, на самом деле нет причин для его знать о User
вообще.
Есть ли какие-то трюки, с которыми я могу просто спеть их вместе и представить их пользователю как одну модель или мне нужно сделать это вручную.
Ответы
Ответ 1
Вы можете использовать POST и PUT с помощью @kahlo, если вы также переопределяете методы создания и обновления вашего сериализатора.
Для такой модели профиля:
class Profile(models.Model):
user = models.OneToOneField(User)
avatar_url = models.URLField(default='', blank=True) # e.g.
Здесь пользовательский сериализатор, который как считывает, так и записывает дополнительные поля (-и) профиля:
class UserSerializer(serializers.HyperlinkedModelSerializer):
# A field from the user profile:
avatar_url = serializers.URLField(source='profile.avatar_url', allow_blank=True)
class Meta:
model = User
fields = ('url', 'username', 'avatar_url')
def create(self, validated_data):
profile_data = validated_data.pop('profile', None)
user = super(UserSerializer, self).create(validated_data)
self.update_or_create_profile(user, profile_data)
return user
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile', None)
self.update_or_create_profile(instance, profile_data)
return super(UserSerializer, self).update(instance, validated_data)
def update_or_create_profile(self, user, profile_data):
# This always creates a Profile if the User is missing one;
# change the logic here if that not right for your app
Profile.objects.update_or_create(user=user, defaults=profile_data)
Полученный API представляет собой плоский пользовательский ресурс, если требуется:
GET /users/5/
{
"url": "http://localhost:9090/users/5/",
"username": "test",
"avatar_url": "http://example.com/avatar.jpg"
}
и вы можете включить поле профиля avatar_url
в запросах POST и PUT. (И DELETE в пользовательском ресурсе также удалит свою модель профиля, хотя это просто нормальный каскад удаления Django.)
Логика здесь всегда будет создавать модель профиля для пользователя, если она отсутствует (при любом обновлении). С пользователями и профилями, возможно, что вы хотите. Для других отношений это может быть не так, и вам нужно будет изменить логику обновления или создания. (Вот почему DRF автоматически не пишет через вложенные отношения для вас.)
Ответ 2
Я только наткнулся на это; Мне еще предстоит найти хорошее решение, особенно для написания моих моделей User
и UserProfile
. В настоящее время я сжимаю свои сериализаторы вручную, используя SerializerMethodField
, который очень раздражает, но он работает:
class UserSerializer(serializers.HyperlinkedModelSerializer):
mobile = serializers.SerializerMethodField('get_mobile')
favourite_locations = serializers.SerializerMethodField('get_favourite_locations')
class Meta:
model = User
fields = ('url', 'username', 'first_name', 'last_name', 'email', 'mobile', 'favourite_locations')
def get_mobile(self, obj):
return obj.get_profile().mobile
def get_favourite_locations(self, obj):
return obj.get_profile().favourite_locations
Это ужасно ручно, но вы в конечном итоге:
{
"url": "http://example.com/api/users/1",
"username": "jondoe",
"first_name": "Jon",
"last_name": "Doe",
"email": "[email protected]",
"mobile": "701-680-3095",
"favourite_locations": [
"Paris",
"London",
"Tokyo"
]
}
Что, я думаю, это то, что вы ищете.
Ответ 3
Я бы выполнил модификации в UserPSerializer
, поскольку поля не будут расти:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'first_name', 'last_name', 'email')
class UserPSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.CharField(source='user.url')
username = serializers.CharField(source='user.username')
first_name = serializers.CharField(source='user.first_name')
last_name = serializers.CharField(source='user.last_name')
email = serializers.CharField(source='user.email')
class Meta:
model = UserProfile
fields = ('mobile', 'favourite_locations',
'url', 'username', 'first_name', 'last_name', 'email')