ValueError when trying to create through Django REST Framework - python

I get a ValueError Cannot assign "[]": "Match.stats" must be a "Stats" instance. when I try and create a match through the browsable API but can create one through the shell just fine.
If I remove the HyperlinkedRelatedField from the MatchSerializer it can create just fine.
models.py
class Player(models.Model):
name = models.CharField(max_length=30)
account = models.IntegerField()
place = models.CharField(max_length=30)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='players')
def __str__(self):
return self.name
class Match(models.Model):
game = models.IntegerField()
length = models.IntegerField()
win = models.BooleanField()
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name='matches')
def __str__(self):
return str(self.game)
class Stats(models.Model):
goals = models.IntegerField()
assists = models.IntegerField()
time = models.IntegerField()
match = models.OneToOneField(Match, on_delete=models.CASCADE, related_name='stats')
def __str__(self):
return '{} {}'.format(str(self.goals), str(self.match))
class Team(models.Model):
possession = models.IntegerField()
goals = models.IntegerField()
assists = models.IntegerField()
extra = models.BooleanField(default=False)
match = models.OneToOneField(Match, on_delete=models.CASCADE, related_name='teams')
def __str__(self):
return '{} - {}'.format(str(self.possession), str(self.match))
serializer.py
class UserSerializer(serializers.ModelSerializer):
players = serializers.HyperlinkedRelatedField(many=True, view_name='players-detail', queryset=Player.objects.all())
class Meta:
model = User
fields = ('id', 'username', 'email', 'first_name', 'last_name', 'players')
class PlayerSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
matches = serializers.HyperlinkedRelatedField(many=True, view_name='matches-detail', queryset=Match.objects.all())
class Meta:
model = Player
fields = ('id', 'name', 'account', 'place', 'user', 'matches')
class MatchSerializer(serializers.ModelSerializer):
player = serializers.ReadOnlyField(source='player.name')
stats = serializers.HyperlinkedRelatedField(many=True, view_name='stats-detail', queryset=Stats.objects.all())
teams = serializers.HyperlinkedRelatedField(many=True, view_name='teams-detail', queryset=Team.objects.all())
class Meta:
model = Match
fields = ('id', 'game', 'length', 'win', 'player', 'stats', 'teams')
class StatsSerializer(serializers.ModelSerializer):
match = serializers.ReadOnlyField(source='match.game')
class Meta:
model = Stats
fields = ('id', 'goals', 'assists', 'time', 'match')
class TeamSerializer(serializers.ModelSerializer):
match = serializers.ReadOnlyField(source='match.game')
class Meta:
model = Team
fields = ('id', 'possession', 'goals', 'assists', 'extra', 'match')
I can go into python manage.py shell and create a match just fine.
>>>m = Match(game=12345, length=5674, win=True, player=player1) # a previously queried player
>>>m.save()
I'm just a little confused what is going

By default, HyperlineRelatedField allow both read and write operation, so I think you need make it read-only true:
class MatchSerializer(serializers.ModelSerializer):
player = serializers.ReadOnlyField(source='player.name')
stats = serializers.HyperlinkedRelatedField(view_name='stats-detail', read_only=True)
teams = serializers.HyperlinkedRelatedField(view_name='teams-detail', read_only=True)
class Meta:
model = Match
fields = ('id', 'game', 'length', 'win', 'player', 'stats', 'teams')
Also, you don't need to add many=True, because both teams and stats are OneToOne relations. So one entry for both tables will be created for each match.

Related

How to create a graphql mutation with a relation in Django

I have a problem with creating mutations in graphql that contain a relation. I don't know where to start.
For example - three classes of models:
class HotelGuests(models.Model):
id = models.UUIDField(primary_key=True,
default=uuid.uuid4,
editable=False)
name = models.CharField(max_length=46, default='noname')
lastname = models.CharField(max_length=46, default='noname')
email = models.EmailField(max_length=254)
tel_number = models.CharField(max_length=12, blank=True, null=True)
class Rooms(models.Model):
room_number = models.PositiveSmallIntegerField()
min_vacancy = models.PositiveSmallIntegerField(default=1)
max_vacancy = models.PositiveSmallIntegerField(default=7)
class Reservations(models.Model):
BOOKING_STATUS = {
(0, 'Do zatwierdzenia'),
(1, 'Zatwierdzona'),
(2, 'Zrealizowana'),
(3, 'Anulowana'),
}
price = models.FloatField(default=0)
prepayment_price = models.FloatField(default=0, blank=True, null=True)
number_of_guests = models.PositiveSmallIntegerField()
date_from = models.DateField(default=now)
date_to = models.DateField(default=now)
description = models.TextField(blank=True, null=True)
booking_status = models.PositiveSmallIntegerField(default=0, choices=BOOKING_STATUS)
hotel_guest = models.ForeignKey(HotelGuests, on_delete=models.CASCADE)
room = models.ForeignKey(Rooms, on_delete=models.SET_NULL, blank=True, null=True)
Three classes of graphql types:
class HotelGuestType(DjangoObjectType):
class Meta:
model = HotelGuests
fields = ('id', 'name', 'lastname', 'email', 'tel_number')
class RoomType(DjangoObjectType):
class Meta:
model = Rooms
fields = ('id', 'room_number', 'min_vacancy', 'max_vacancy')
class ReservationType(DjangoObjectType):
class Meta:
model = Reservations
fields = ('id',
'price',
'prepayment_price',
'number_of_guests',
'date_from',
'date_to',
'description',
'booking_status',
'hotel_guest',
'room',)
And three classes of node:
class HotelGuestNode(DjangoObjectType):
class Meta:
model = HotelGuests
filter_fields = ['id',
'name',
'lastname',
'email',
'tel_number']
interfaces = (relay.Node, )
class RoomNode(DjangoObjectType):
class Meta:
model = Rooms
filter_fields = ['id',
'room_number',
'min_vacancy',
'max_vacancy']
interfaces = (relay.Node, )
class ReservationNode(DjangoObjectType):
in_room = relay.ConnectionField(RoomNode, description='InRoom')
booked_by = relay.ConnectionField(HotelGuestNode, description='BookedBy')
#resolve_only_args
def resolve_in_room(self):
return self.in_room.all()
class Meta:
model = Reservations
filter_fields = ['id',
'price',
'prepayment_price',
'number_of_guests',
'date_from',
'date_to',
'description',
'booking_status']
interfaces = (relay.Node,)
How could I make a mutation out of this in which I would create a reservation object connected to a hotel guest and a room?
when you declare your hotel_guest and room fields inside your Reservations Model you can create a related_name like this:
hotel_guest = models.ForeignKey(HotelGuests, on_delete=models.CASCADE, related_name="hotel_reservation")
room = models.ForeignKey(Rooms, on_delete=models.SET_NULL, blank=True, null=True, related_name="hotel_room")
Then you can access it in your HotelGuestType or your RoomType using the respective related_name.
This allows you to use them in your Query

How build a category model after building legacy model in Django REST Framework

Hello, I have a question about improving legacy models.
The Material model is old model, and i want to make category by using new model 'type'. But i have a little problem with when i use admin site. In admin site, i hope to choose the 'type' first, and upload data .. how can i make better
models
# new model
class MaterialType(BaseModel):
type = models.CharField(choices=MaterialTypeChoice.choices, max_length=50, null=True, blank=True)
def __str__(self):
return self.type
# old model
class Material(models.Model):
type = models.ForeignKey(MaterialType, verbose_name=, null=True, blank=True, on_delete=models.SET_NULL)
name = models.CharField max_length=50, null=True, blank=True)
size = models.CharField(max_length=50, null=True, blank=True)
unit = models.CharField(max_length=5, null=True, blank=True)
price_unit = models.IntegerField(null=True, blank=True)
def __str__(self):
return self.name
serializers
# new
class MaterialTypeListSerializer(serializers.ModelSerializer):
class Meta:
model = MaterialType
fields = ["type"]
# old
class MaterialListSerializer(serializers.ModelSerializer):
class Meta:
model = Material
fields = ["id", "type", "name", "size", "unit", "price_unit"]
views
# new
class MaterialTypeList(ListCreateAPIView):
queryset = MaterialType.objects.all()
serializer_class = MaterialTypeListSerializer
# old
class MaterialList(ListAPIView):
queryset = Material.objects.all()
filter_class = MaterialFilter
serializer_class = MaterialListSerializer
admin
#admin.register(Material)
class MaterialAdmin(ImportExportModelAdmin):
list_display = ["name", "size", "unit", "price_unit"]
list_display_links = ("name",)
list_filter = ("type",)
list_per_page = 10
# list_editable = ('type',)
search_fields = ("name", "size")
resource_class = MaterialResource
#admin.register(MaterialType)
class MaterialTypeAdmin(ImportExportModelAdmin):
list_display = ["type"]
list_filter = ("type",)
list_per_page = 10
# list_editable = ('type',)

Why TabularInline looks different on differen sites. How to control its apperence

I have 2 different registers on my admin panel and on one TabularInline allows multiple choice:
and on another not:
Where does the difference come from? How can I control it?
Update:
I have a table movies which is connected to the table person via intermediate table. Table Person has name and role (actor, writer, director). What I am trying to do is to allow in admin panel to see and edit both, e.g. Movies should have 3 different panels, where one can add actor, writer and director. Actor should have name and movie to add.
So far I was able to make 3 panels for movies, but they do not allow several choices and show all names, but not only actors in actors panel (see second picture). Also when I save them, they do not get role - actor. Actors allow to choose several movies, but when I save them, admin blocks me saying "Film work person with this Movie already exists."
My models (I only show part for films and actors, writers and director are the same):
class FilmWorkPerson(models.Model):
id = models.AutoField(primary_key=True, editable=False)
movie = models.OneToOneField('FilmWork', models.CASCADE, unique=False)
person = models.ForeignKey('Person', models.CASCADE, unique=False)
created_on = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'film_work_person'
unique_together = (('movie', 'person'),)
class FilmWork(models.Model):
# what we have from sql
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255)
people = models.ManyToManyField('Person', through=FilmWorkPerson)
class Person(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(_('ФИО'), max_length=100)
role = models.CharField(_('роль'), max_length=100)
created_on = models.DateTimeField(auto_now =True)
movie = models.ManyToManyField(FilmWork, through=FilmWorkPerson)
class Meta:
db_table = 'person'
def __str__(self):
return self.name
class ActorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role='actor')
class Actor(Person):
objects = ActorManager()
class Meta:
proxy = True
verbose_name = _('actor')
verbose_name_plural = _('actors')
my admin.py:
class PersonInlineAdmin(admin.TabularInline):
model = Person.movie.through
class ActorInlineFormSet(BaseInlineFormSet):
def get_queryset(self):
qs = super(ActorInlineFormSet, self).get_queryset().filter(person__role__iexact='actor')
self._queryset = qs
return self._queryset
class ActorInlineAdmin2(admin.TabularInline):
model = FilmWork.people.through
formset = ActorInlineFormSet
verbose_name = 'actor'
verbose_name_plural = 'actors'
#admin.register(FilmWork)
class FilmworkAdmin(admin.ModelAdmin):
# отображение полей в списке
list_display = ('title', 'ratings', 'created_on', 'actors', 'writers', 'director',
'film_creation_date', 'age_limit', 'link')
def actors(self, obj):
actors = FilmWork.objects.filter(person__role__iexact='actor', title=obj).values_list('person__name', flat=True)
return ", ".join([a for a in actors])
def writers(self, obj):
writers = FilmWork.objects.filter(people__role__iexact='writer', title=obj).values_list('people__name', flat=True)
return ", ".join([a for a in writers])
def director(self, obj):
director = FilmWork.objects.filter(people__role__iexact='director', title=obj).values_list('people__name', flat=True)
return ", ".join([a for a in director])
list_filter = ('genre','film_creation_date', 'age_limit', 'link')
fields = ('title', 'plot', 'ratings', 'film_creation_date', 'age_limit', 'link')
search_fields = ('title', 'plot', 'id', 'film_creation_date', 'age_limit', 'link')
inlines = (GenreInlineAdmin, ActorInlineAdmin2, DirectorInlineAdmin2, WriterInlineAdmin2, )
#admin.register(Actor)
class Actor(admin.ModelAdmin):
list_display = ('name', )
fields = ('name', )
search_fields = ('name', 'movie')
list_filter = ('movie',)
inlines = (PersonInlineAdmin,)
I'm not sure if I understand your question clearly, but I'm guessing it's because of the different relationship types between the models, models.ForeignKey for the Genre vs models.ManyToManyField for the Movie.
If you can provide more details on what you're trying to achieve, I might be able to help.

Django REST Framework: NestedSerializer in OneToMany relation

I have 2 related models:
class Systems(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=2000, null=True, blank=True)
class Teams(models.Model):
system = models.ForeignKey(Systems, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
description = models.CharField(max_length=2000, null=True, blank=True)
And 2 serializers:
class System_serializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField
class Meta:
model = Systems
fields = ('id', 'name', 'description')
def create(self, validated_data):
return Systems.objects.create(**validated_data)
def update(self, system, validated_data):
system.name = validated_data.get('name', system.name)
system.description = validated_data.get('description', system.description)
system.save()
return system
second:
class Team_serializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField
system_id = System_serializer()
class Meta:
model = Teams
fields = ('id', 'system_id', 'name', 'description')
def create(self, validated_data):
return Teams.objects.create(**validated_data)
def update(self, team, validated_data):
team.name = validated_data.get('name', team.name)
team.description = validated_data.get('description', team.description)
team.system_id = validated_data.get('system_id', team.system_id)
team.save()
return team
In 'team' entity I just want to have only 'System' ID, not the whole 'System'
How can I use the System_serializer, to serialize 'system_id' in 'team' object?
Thanks!
ADD
I tried to use SlugRelatedField:
class Team_serializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField
system_id = serializers.SlugRelatedField(queryset=Systems.objects.all(), slug_field='id')
class Meta:
model = Teams
fields = ('id', 'system_id', 'name', 'description')
But got
'int' object has no attribute 'id'
ADD2
Tried to use PrimatyKeyRelatedField:
class Team_serializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField
system_id = serializers.PrimaryKeyRelatedField(queryset=Systems.objects.all())
class Meta:
model = Teams
fields = ('id', 'system_id', 'name', 'description')
def create(self, validated_data):
return Teams.objects.create(**validated_data)
but got on create
Field 'id' expected a number but got <Systems: Systems object (2)>.

Django REST Framework many to many create and update

models.py:
class Book(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500)
image = models.ImageField(height_field="height_field", width_field="width_field")
height_field = models.IntegerField(default=255)
width_field = models.IntegerField(default=255)
price = models.FloatField()
edition = models.CharField(max_length=100)
no_of_page = models.IntegerField()
country = models.CharField(max_length=50)
publication = models.ForeignKey(Publication, on_delete=models.CASCADE)
authors = models.ManyToManyField(Author, through='AuthorBook')
ratings = GenericRelation(Rating, related_query_name='books')
class AuthorBook(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=100)
biography = models.TextField(max_length=500)
image = models.ImageField(height_field="height_field", width_field="width_field")
height_field = models.IntegerField(default=255)
width_field = models.IntegerField(default=255)
serializers.py
class AuthorListSerializer(ModelSerializer):
url = author_detail_url
class Meta:
model = Author
fields = [
'url',
'id',
'name',
]
class BookCreateUpdateSerializer(ModelSerializer):
authors = AuthorListSerializer(many=True, read_only=True)
def create(self, validated_data):
#code
def update(self, instance, validated_data):
#code
class Meta:
model = Book
fields = [
'name',
'description',
'price',
'edition',
'no_of_page',
'country',
'publication',
'authors',
]
views.py
class BookCreateAPIView(CreateAPIView):
queryset = Book.objects.all()
serializer_class = BookCreateUpdateSerializer
I am working for implementing Django REST Framework. I have two models Book and Author. But django create api view don't show authors field drop down. I checked many solution DRF for many to many field. Please help me to show authors field and write create() and update function. If you see DRF api page, it will be clear.
This is because of read_only=True. Try to remove this argument for bookserializer's authors field:
class BookCreateUpdateSerializer(ModelSerializer):
authors = AuthorListSerializer(many=True)

Categories