Django DRF - Include Foreign-Key field to serializer - django

How do I add 'platform_description' field to be part of battlesSerializer result?
Model
class battles(models.Model):
# Fields
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
wager = models.FloatField(max_length=30)
battle_rules = models.CharField(max_length=30,null = True,blank = True)
accepting_time = models.DateTimeField()
offer_expiration_time = models.DateTimeField(null = True,blank = True)
battle_time = models.DateTimeField(null = True,blank = True)
rake = models.FloatField()
# Relationship Fields
platform_id = models.ForeignKey('platforms.game_platforms', related_name='seal')
class Meta:
ordering = ('-created',)
def __unicode__(self):
return u'%s' % self.id
def get_absolute_url(self):
return reverse('platforms_battles_detail', args=(self.id,))
def get_update_url(self):
return reverse('platforms_battles_update', args=(self.id,))
class game_platforms(models.Model):
# Fields
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
platform_description = models.CharField(max_length=30)
class Meta:
ordering = ('-created',)
def __unicode__(self):
return u'%s' % self.id
def __str__(self):
return '%s' % self.platform_description
def get_absolute_url(self):
return reverse('platforms_game_platforms_detail', args=(self.id,))
def get_update_url(self):
return reverse('platforms_game_platforms_update', args=(self.id,))
Serializer
class battlesSerializer(serializers.ModelSerializer):
class Meta:
model = models.battles
fields = (
'id',
'created',
'last_updated',
'wager',
'battle_rules',
'accepting_time',
'offer_expiration_time',
'battle_time',
'rake',
)
class game_platformsSerializer(serializers.ModelSerializer):
class Meta:
model = models.game_platforms
fields = (
'id',
'created',
'last_updated',
'platform_description',
)

You can use SerializerMethodField:
class battlesSerializer(serializers.ModelSerializer):
platform_description = serializers.SerializerMethodField()
def get_platform_description(self, obj):
# Use a try - except block if needed
return obj.platform_id.platform_description
class Meta:
model = models.battles
fields = (
'id',
'created',
'last_updated',
'wager',
'battle_rules',
'accepting_time',
'offer_expiration_time',
'battle_time',
'rake',
'platform_description', # add this field
)
Also, you could take a look over PEP8's naming conventions to write an elegant python code.

Related

error datefiled input when add new record

I have next problem.
I have models: Period and CompletedWork
class Period(models.Model):
date = models.DateField()
def __repr__(self):
return self.date
class CompletedWork(models.Model):
period = models.ForeignKey(directory.Period,
on_delete=models.SET('deleted date'),
)
worker = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET('deleted worker'),
related_name='worker_do', default=settings.AUTH_USER_MODEL
)
work_done = models.ForeignKey(directory.WorksType, on_delete=models.SET('deleted works type'))
work_scope = models.FloatField(blank=True, null=True)
work_notes = models.CharField(_("Comments"), max_length=70, blank=True, null=True, )
record_author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET('deleted user'),
related_name='record_author', auto_created=True,
)
record_date = models.DateTimeField(auto_now=True)
checked_by_head = models.BooleanField(default=False)
active = models.BooleanField(default=True)
def __repr__(self):
return f'{self.period}, {self.worker}, {self.work_done}'
def __str__(self):
return self.__repr__()
def is_active(self):
if self.active:
return True
return False
def __str__(self):
return str(self.__repr__())
In the forms I make a widget for Date input:
class CompletedWorkForm(forms.ModelForm):
class Meta:
model = CompletedWork
fields = (
'period',
'worker',
'work_done',
'work_scope',
'work_notes',
)
widgets = {
'period': DatePickerInput(),
}
widget.py looks like this:
class DatePickerInput(forms.DateInput):
input_type = 'date'
my view:
class CreateCompletedWorkView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = CompletedWork
form_class = CompletedWorkForm
template_name = 'completed_work_add.html'
success_url = reverse_lazy('completed_work_list')
success_message = f'Record successfully added'
def get_form_kwargs(self):
kwargs = super(CreateCompletedWorkView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.record_author = self.request.user
return super().form_valid(form)
And now I have a problem creating a new record:
"Select a valid choice. That choice is not one of the available choices."
Please tell me how can I fixed. I understand, that maybe problem with the format that I get after POST

How can I show the StringRelatedField instead of the Primary Key while still being able to write-to that field using Django Rest Framework?

Models:
class CrewMember(models.Model):
DEPARTMENT_CHOICES = [
("deck", "Deck"),
("engineering", "Engineering"),
("interior", "Interior")
]
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.EmailField()
department = models.CharField(max_length=12, choices=DEPARTMENT_CHOICES)
date_of_birth = models.DateField()
join_date = models.DateField()
return_date = models.DateField(null=True, blank=True)
leave_date = models.DateField(null=True, blank=True)
avatar = models.ImageField(null=True, blank=True)
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
class RosterInstance(models.Model):
date = models.DateField(default=timezone.now)
deckhand_watchkeeper = models.ForeignKey(CrewMember, on_delete=models.PROTECT, null=True, related_name="deckhand_watches")
night_watchkeeper = models.ForeignKey(CrewMember, on_delete=models.PROTECT, null=True, related_name="night_watches")
def __str__(self):
return self.date.strftime("%d %b, %Y")
Views:
class CrewMemberViewSet(viewsets.ModelViewSet):
queryset = CrewMember.objects.all()
serializer_class = CrewMemberSerializer
filter_backends = [SearchFilter]
search_fields = ["department"]
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.active = False
instance.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class RosterInstanceViewSet(viewsets.ModelViewSet):
queryset = RosterInstance.objects.all()
serializer_class = RosterInstanceSerializer
Serializers:
class CrewMemberSerializer(serializers.ModelSerializer):
class Meta:
model = CrewMember
fields = "__all__"
class RosterInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = RosterInstance
fields = "__all__"
The resulting data looks like this:
{
"id": 2,
"date": "2020-12-09",
"deckhand_watchkeeper": 1,
"night_watchkeeper": 3
}
But I want it to look like this:
{
"id": 2,
"date": "2020-12-09",
"deckhand_watchkeeper": "Joe Soap",
"night_watchkeeper": "John Smith"
}
I can achieve the above output by using StringRelatedField in the RosterInstanceSerializer but then I can no longer add more instances to the RosterInstance model (I believe that is because StringRelatedField is read-only).
Because StringRelaredField is always read_only, you can use SlugRelatedField instead:
class RosterInstanceSerializer(serializers.ModelSerializer):
deckhand_watchkeeper = serializers.SlugRelatedField(
slug_field='deckhand_watchkeeper'
)
night_watchkeeper = serializers.SlugRelatedField(
slug_field='night_watchkeeper'
)
class Meta:
model = RosterInstance
fields = ['id', 'date', 'deckhand_watchkeeper', 'night_watchkeeper']
I was created a WritableStringRelatedField to do that.
class WritableStringRelatedField(serializers.SlugRelatedField):
def __init__(self, display_field=None, *args, **kwargs):
self.display_field = display_field
# Set what attribute to be represented.
# If `None`, use `Model.__str__()` .
super().__init__(*args, **kwargs)
def to_representation(self, obj):
# This function controls how to representation field.
if self.display_field:
return getattr(obj, self.display_field)
return str(obj)
def slug_representation(self, obj):
# It will be called by `get_choices()`.
return getattr(obj, self.slug_field)
def get_choices(self, cutoff=None):
queryset = self.get_queryset()
if queryset is None:
# Ensure that field.choices returns something sensible
# even when accessed with a read-only field.
return {}
if cutoff is not None:
queryset = queryset[:cutoff]
return OrderedDict([
(
self.slug_representation(item),
# Only this line has been overridden,
# the others are the same as `super().get_choices()`.
self.display_value(item)
)
for item in queryset
])
Serializers:
class RosterInstanceSerializer(serializers.ModelSerializer):
deckhand_watchkeeper = WritableStringRelatedField(
queryset=CrewMember.objects.all(),
slug_field='id',
label='Deckhand Watchkeeper',
)
night_watchkeeper = WritableStringRelatedField(
queryset=CrewMember.objects.all(),
slug_field='id',
label='Night Watchkeeper',
)
class Meta:
model = RosterInstance
fields = "__all__"

Django / Customized ModelChoiceField: how label_from_instance works?

I use ModelChoiceField with initial value just for display (readonly field).
But value displayed is model id and I want to display customized value that will be a concatenation of 3 fields. I've override ____str____ method but it is not applyed.
I try to make customize ModelChoiceFiedl to define label_from_instance that seems to be the way to do what I want but it is not applyed...
models.py
class Utilisateur(SafeDeleteModel):
_safedelete_policy = SOFT_DELETE_CASCADE
uti_ide = models.AutoField(primary_key = True)
# pro_ide = models.ForeignKey(Projet, on_delete = models.CASCADE) # related project
projets = models.ManyToManyField(Projet, through='UtilisateurProjet')
uti_nom = models.CharField("Nom", max_length=20)
uti_pre = models.CharField("Prénom", max_length=20)
uti_mai = models.CharField("Email", max_length=40)
uti_sit = models.CharField("Equipe", max_length=20, null=True, blank=True)
uti_pro = models.CharField("Fonction/profil", max_length=200, null=True, blank=True)
uti_log = models.CharField("Log utilisateur", max_length=20, null=True, blank=True)
uti_dat = models.DateTimeField("Date log",auto_now_add=True, null=True, blank=True)
log = HistoricalRecords()
#classmethod
def options_list(cls,pro_ide):
# projet = Projet.objects.get(pro_ide=pro_ide)
# utilisateurs = Utilisateur.objects.filter(pro_ide=projet.pro_ide)
utilisateurs = Utilisateur.objects.filter(projets__pro_ide=pro_ide)
the_opts_list = [(utilisateur.uti_ide, utilisateur.uti_nom+', '+utilisateur.uti_pre) for utilisateur in utilisateurs]
the_opts_list.insert(0, (None, ''))
return the_opts_list
class Meta:
db_table = 'tbl_uti'
verbose_name_plural = 'Utilisateurs'
ordering = ['uti_ide']
def __str__(self):
return f"{self.uti_nom}, {self.uti_pre} ({self.uti_mai})"
forms.py
class UtilisateurModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
print('label')
return obj.uti_nom+', '+obj.uti_pre+' ('+obj.uti_mai+')'
class UtilisateurProjetUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(UtilisateurProjetUpdateForm, self).__init__(*args, **kwargs)
# print('kwargs',self.request)
# print('projet',self.request['projet'])
# print('utilisateur',self.request['utilisateur'])
PROJETS = Projet.objects.all()
UTILISATEURS = Utilisateur.objects.all()
self.fields["pro_ide"] = forms.ModelChoiceField(queryset = PROJETS, label = "Nom projet", widget = forms.HiddenInput(), initial = Projet.objects.get(pro_ide=self.request['projet']))
self.fields["uti_ide"] = UtilisateurModelChoiceField(queryset = UTILISATEURS, label = "Nom, prénom de l'utilisateur", widget = forms.TextInput(attrs={'readonly':'readonly'}), initial = Utilisateur.objects.get(uti_ide=self.request['utilisateur']),) #,to_field_name="uti_nom"
class Meta:
model = UtilisateurProjet
fields = ('pro_ide','uti_ide',)
in forms.py under that last class Meta: you need to add:
field_classes = {
'pro_ide': UtilisateurModelChoiceField,
'uti_ide': UtilisateurModelChoiceField,
}

creating order from orderitem drf

i am trying to create order from orderitem. problem is how can i pass orderitem object to order.item field:
models.py:
class OrderItem(models.Model):
image_number = models.CharField(max_length=20)
title = models.CharField(max_length=20)
image_size = models.CharField(max_length=50)
file_type = models.CharField(max_length=20)
price = models.CharField(max_length=50)
def __str__(self):
return self.title
class Order(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, blank=True,null=True)
items = models.ManyToManyField(OrderItem)
start_date = models.DateTimeField(auto_now_add=True)
ordered_date = models.DateField(auto_now_add=False,blank=True,null=True)
ordered = models.BooleanField(default=False)
def __str__(self):
return str(self.user)
serializers.py:
class AddtocartSerializers(serializers.ModelSerializer):
class Meta:
model = OrderItem
fields = ['image_number','title','image_size','file_type','price']
class CartSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all())
items = serializers.ListField(child=serializers.PrimaryKeyRelatedField(queryset=OrderItem.objects.all(), required=True), required=True)
ordered_date = serializers.DateField()
class Meta:
model = Order
fields = ['user', 'items', 'ordered_date']
views.py:
class AddtocartView(generics.CreateAPIView):
authentication_classes = []
permission_classes = [IsAuthenticated]
pagination_class = None
queryset = OrderItem.objects.all()
serializer_class = AddtocartSerializers
def perform_create(self, serializer):
new_order_item = serializer.save()
user=CustomUser.objects.filter(id=self.request.user.id).first()
new_order = Order.objects.create(user=user)
new_order.items.add(new_order_item)
def __str__(self):
return self.user
class CartView(generics.ListAPIView):
authentication_classes = []
permission_classes = []
pagination_class = None
queryset=Order.objects.all()
serializer_class = CartSerializer
urls.py:
path('addtocart/',views.AddtocartView.as_view(),name='addtocart'),
path('cart/',views.CartView.as_view(),name='cart'),
customuser model:
class CustomUser(AbstractBaseUser, PermissionsMixin):
USER_TYPE_CHOICES = (
('user','user'),
('freelance_photographer', 'freelance_photographer'),
('photographer', 'photographer'),
('client', 'client'),
)
user_type = models.CharField(choices=USER_TYPE_CHOICES, null=True,max_length=20)
email = models.EmailField(max_length = 100, unique = True)
first_name = models.CharField(max_length = 100, null = True, blank = True)
last_name = models.CharField(max_length = 100, null = True, blank = True)
is_staff = models.BooleanField(default = False)
is_superuser = models.BooleanField(default = False)
is_active = models.BooleanField(default = True)
last_login = models.DateTimeField(null = True, blank = True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = "email"
EMAIL_FIELD = "email"
REQUIRED_FIELD = []
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % (self.pk)
error:
Direct assignment to the forward side of a many-to-many set is prohibited. Use items.set() instead.
Is there any better way to create order?
You can create order with order item this way:
class AddtocartView(generics.CreateAPIView):
authentication_classes = []
permission_classes = [IsAuthenticated]
pagination_class = None
queryset = OrderItem.objects.all()
serializer_class = AddtocartSerializers
def perform_create(self, serializer):
new_order_item = serializer.save()
new_order = Order.objects.create()
new_order.items.add(new_order_item)

Django rest PUT method to update foreign key data

DRF doc's say's i need to override serializers create and update methods to update and/or create nested data. But when i am trying to edit existing descriptions my code creates a new one. Is there a simple way to detect which descriptions was edit and update only it?
class TaskSerializer(ModelSerializer):
url = HyperlinkedIdentityField(
view_name='tasks:tasks-detail',
lookup_field='pk',
)
author = SlugField(source='author.username')
executor = SlugField(source='executor.username')
descriptions = DescriptionSerializer(many=True)
class Meta:
model = Task
fields = (
'pk',
'url',
'title',
'project',
'status',
'author',
'executor',
'descriptions'
)
def create(self, validated_data):
descriptions_data = validated_data.pop('descriptions', None)
author = validated_data.pop('author', None)
executor = validated_data.pop('executor', None)
try:
task_author = User.objects.get(username=author['username'])
task_executor = User.objects.get(username=executor['username'])
except User.DoesNotExist:
raise ValidationError(
_("Такого пользователя не сущетсвует!"))
task = Task.objects.create(author=task_author, executor=task_executor, **validated_data)
if descriptions_data:
for description in descriptions_data:
description, created = Description.objects.get_or_create(
text=description['text'],
task=task
)
task.descriptions.add(description)
return task
def update(self, instance, validated_data):
descriptions_data = validated_data.pop('descriptions', None)
instance.title = validated_data.get('title', instance.title)
instance.project = validated_data.get('project', instance.project)
instance.status = validated_data.get('status', instance.status)
author = validated_data.pop('author', None)
executor = validated_data.pop('executor', None)
try:
if author:
task_author = User.objects.get(username=author['username'])
instance.author = task_author
if executor:
task_executor = User.objects.get(username=executor['username'])
instance.executor = task_executor
except User.DoesNotExist:
raise ValidationError(
_("Такого пользователя не сущетсвует!"))
descriptions_list = []
if descriptions_data:
for description in descriptions_data:
description, created = Description.objects.get_or_create(
text=description["text"],
task=instance
)
descriptions_list.append(description)
instance.descriptions.set(descriptions_list)
instance.save()
return instance
class DescriptionSerializer(ModelSerializer):
class Meta:
model = Description
fields = (
'pk',
'text',
)
class Task(models.Model):
STATUS_CHOICES = (
(1, _("В процессе разработки")),
(2, _("В процессе тестирования")),
(3, _("На ревью")),
(4, _("Открыта")),
(5, _("Закрыта"))
)
title = models.CharField(_('Название'), max_length=60)
project = models.CharField(_('Название проекта'), max_length=60)
status = models.IntegerField(_('Статус'), choices=STATUS_CHOICES,
default=4)
author = models.ForeignKey(settings.AUTH_USER_MODEL,
max_length=60, on_delete=models.CASCADE,
related_name='author',
verbose_name=_('Автор'),
validators=[validate_user])
executor = models.ForeignKey(settings.AUTH_USER_MODEL,
max_length=60, on_delete=models.CASCADE,
related_name='executor',
verbose_name=_('Исполнитель'),
validators=[validate_user])
def __str__(self):
return self.title
def __unicode__(self):
return u'{}'.format(self.title)
class Meta:
verbose_name = _('Задача')
verbose_name_plural = _('Задачи')
class Description(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE,
related_name='descriptions')
text = models.TextField(_('Описание'))
def __str__(self):
return '#{}'.format(self.pk)
def __unicode__(self):
return u'#{}'.format(self.pk)
class Meta:
verbose_name = _('Описание')
verbose_name_plural = _('Описания')