Django - Display TextChoices - django

Imagine there is a model:
class CeleryStatus(models.Model):
class Code(models.TextChoices):
PENDING = 'pending', 'Pending'
PROGRESS = 'progress', 'In progress'
SUCCESS = 'success', 'Success'
code = models.TextField(unique=True, choices=Code.choices)
name = models.TextField() # editable, for frontend
description = models.TextField()
Register the model in admin:
#admin.register(CeleryStatus)
class CeleryStatusAdmin(admin.ModelAdmin):
list_display = ('code', 'name')
The problem: in "Code" column there are not actual codes (progress) but like descriptions (In progress).
How to display actual code (what is written into database table) (progress instead of In progress)?

Related

How to send a post request via postman to nested writable serializer in django-rest-framework?

I'm really struggling with sending my data with postman in correct form to my Django backend. I followed the approach in the Django documentation for a writeable nested Seralizer and adapted it to my case. If I pass the data to my serializer via the shell, everything works and I can create the two object instances Story and File. But if I try to do the same with post man, it is not successful and receive the following error Message: Got AttributeError when attempting to get a value for field 'file' on serializer 'StoryCreateUpdateSerializer'. The serializer field might be named incorrectly and not match any attribute or key on the 'Story' instance. Original exception text was: 'Story' object has no attribute 'file'.
Successfull Request via Shell:
>>> data = {
'title': 'HALLLO',
'file': [
{'content': 'Public Service Announcement'},
{'content': 'Public Service Announcement'},
{'content': 'Public Service Announcement'},
],
}
>>> serializer = StoryCreateUpdateSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.save()
Not Successfull Request via Postman. Header: Content-Type: application/json. Body: Raw
{
"title": "Test",
"file": [
{
"content": "Hallo"
}
]
}
My models and Serializers
#MODELS
class Story (models.Model):
title = models.CharField(max_length=100,blank=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
class File(models.Model):
story = models.ForeignKey(Story,on_delete=models.CASCADE, null=True)
content = models.TextField(blank=False, null=True)
#SERIALIZER
class TestFileSerializer (serializers.ModelSerializer):
class Meta:
model = File
fields = ('content',)
class StoryCreateUpdateSerializer (serializers.ModelSerializer):
file = TestFileSerializer(many=True)
class Meta:
model = Story
fields = ('title','file')
def create(self, validated_data):
current_user = self.context["request"].user
title = validated_data.pop('title')
file = validated_data.pop('file')
story_instance = Story.objects.create(author=current_user, title=title)
for file_data in file:
File.objects.create(**file_data, story=story_instance)
return story_instance
#VIEW
class StoryCreateUpdateViewSet(viewsets.ModelViewSet):
serializer_class = StoryCreateUpdateSerializer
queryset = Story.objects.all()
I'am happy for any clarification. As i running out of ideas.
In ForeignKey field of File model, default related_name and related_query_name attributes are 'file_set' not 'file'
So you should do one of these changes on your code:
Method 1:
In File model add related_name and related_query_name attributes to ForeignKey field with values 'file':
story = models.ForeignKey(
Story,
on_delete=models.CASCADE,
null=True,
related_name = 'file',
related_query_name = 'file'
)
Method 2:
In StoryCreateUpdateSerializer change 'file' field to 'file_set':
class StoryCreateUpdateSerializer (serializers.ModelSerializer):
file_set = TestFileSerializer(many=True)
class Meta:
model = Story
fields = ('title','file_set')
def create(self, validated_data):
current_user = self.context["request"].user
title = validated_data.pop('title')
file = validated_data.pop('file')
story_instance = Story.objects.create(author=current_user, title=title)
for file_data in file:
File.objects.create(**file_data, story=story_instance)
return story_instance
In this method, also you've to change your request body properties and substitute 'file' with 'file_set'

Writable nested serializers with inherited django models

I'm struggling to update records with the writeable nested serializers I've created.
There are many Listing categories for a classifieds app I'm creating that each have a few unique attributes, but they also share many attributes. I have a handful of django models that inherit from a parent Listing model, and one of these models, Battery, contains some nested data. So far I've been able to create Battery records but keep getting AttributeErrors when I try to update them.
I've tried to include only the relevant code. Here are my views:
# views.py
class ListingCreateView(CreateAPIView):
queryset = Listing.objects.all()
def get_serializer_class(self):
category = self.request.data['category']
if category == 1:
return PercussionSerializer
elif category == 6:
return BatterySerializer
return ListingSerializer
class ListingUpdateView(UpdateAPIView):
queryset = Listing.objects.all()
def get_serializer_class(self):
category = self.request.data['category']
if category == 1:
return PercussionSerializer
elif category == 6:
return BatterySerializer
return ListingSerializer
here are my models:
# models.py
## Parent model
class Listing(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True, default=0.00)
## One of the child models
class Battery(Listing):
model_name = models.TextField(blank=True, null=True, default="")
color = models.ForeignKey(Color, on_delete=models.CASCADE, blank=True, null=True)
manufacture_year = models.IntegerField(null=True)
## Model for the nested data in Battery model
class Drum(models.Model):
drum_type = models.CharField(max_length=50, blank=True)
size = models.TextField(blank=True, null=True, default="")
battery = models.ForeignKey(Battery, related_name='drums', on_delete=models.CASCADE, null=True)
and here are my serializers:
# serializers.py
class ListingSerializer(serializers.ModelSerializer):
class Meta:
model = Listing
fields = '__all__'
class DrumSerializer(serializers.ModelSerializer):
class Meta:
model = Drum
fields = ['drum_type', 'size', 'carrier', 'stand', 'cover', 'case', 'sold']
class BatterySerializer(serializers.ModelSerializer):
drums = DrumSerializer(many=True)
class Meta:
model = Battery
fields = ['id', 'title', 'description', 'price', 'model_name', 'color', 'manufacture_year', 'drums']
def create(self, validated_data):
drum_data = validated_data.pop('drums')
battery = Battery.objects.create(**validated_data)
for drum_data in drum_data:
Drum.objects.create(battery=battery, **drum_data)
return battery
def update(self, instance, validated_data):
# Update Listing field values
instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('description', instance.description)
instance.price = validated_data.get('price', instance.price)
# Grab the Battery record for this Listing and update its values
instance_battery = Battery.objects.get(pk=instance.pk)
instance_battery.model_name = validated_data.get('model_name', instance_battery.model_name)
instance_battery.color = validated_data.get('color', instance_battery.color)
instance_battery.manufacture_year = validated_data.get('manufacture_year', instance_battery.manufacture_year)
# Check for a list of drums
drum_data = validated_data.pop('drums')
# If it exists
if drum_data:
# Clear the existing drums
instance_battery.drums.clear()
# Create new drums
Drum.objects.bulk_create(
[
Drum(**drum)
for drum in drum_data
],
)
# Save the updated Listing & Battery
instance.save()
instance_battery.save()
# Return the updated Battery
return instance
I feel like I've followed the DRF documentation about writeable nested serializers correctly, but I continue to get this AttributeError when I try to post an update to a Battery record:
AttributeError: Got AttributeError when attempting to get a value for field drums on serializer BatterySerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Listing instance.
Original exception text was: 'Listing' object has no attribute 'drums'.
Judging by the error message I think the Django model inheritance requires a more specific solution that what the DRF documentation provides. Can anybody help me understand how to create the serializers I need to create/update a Battery record that inherits from a Listing model with a nested list of Drum records?
Thanks!
I think the problem appears because the view waits for a Listing instance. I can suggest the following: try to redefine def get_qyeryset(self)
For example:
class ListingUpdateView(UpdateAPIView):
def get_queryset(self):
if self.request.data['category'] == 6:
return Battery.objects.all()
else:
return Listing.objects.all()
def get_serializer_class(self):
category = self.request.data['category']
if category == 1:
return PercussionSerializer
elif category == 6:
return BatterySerializer
return ListingSerializer
Maybe it is not the best way, but it can solve your problem

Django checkbox field

I am trying to add a checkbox that if ticked will make a attribute in my model false.
My model is:
class Topic(models.Model):
"""A topic the user is learning about"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User)
public = False
My forms.py (where the checkbox should go) is:
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
And my function:
def topics(request):
"""Show all topics."""
topics = Topic.objects.filter(owner=request.user).order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
Could you please tell me what I need to change in order that when a checkbox in forms is checked, the public variable becomes True, and then the function Topics displays public topics as well as just the owners.
Thanks
Milo
The models.BooleanField renders itself as a checkbox. Is either True or False. So:
# models.py
class Topic(models.Model):
# ...
public = models.BooleanField(default=True)
# forms.py
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ["text", "public"]
labels = {"text": "", "public": "label for public"}
If you want to also accept null values, then you should use models.NullBooleanField.

Django REST framework serialized field containing unexpected characters in test case environment

I am having two model definitions CTA and VariationCTA as follows:
class CTA(AbstractTimestampClass):
page = models.ForeignKey(Page,
related_name='related_page_ctas')
css_path = models.CharField(max_length=1000)
source_content = models.TextField()
category = models.CharField(max_length=100)
subcategory = models.CharField(max_length=100)
ctype = models.CharField(max_length=25, choices=CTA_TYPE_CHOICES, default=CTA_TYPE_HTML)
deleted = models.BooleanField(default=False)
class VariationCTA(AbstractTimestampClass):
variation = models.ForeignKey(Variation,
related_name='related_variation_ctas')
cta = models.ForeignKey(CTA,
related_name='related_cta_variation_ctas')
target_content = models.TextField()
deleted = models.BooleanField(default=False)
To combine these two models I am making use of a serializer as follows:
class PageCTASerializerForVariation(serializers.ModelSerializer):
variation = serializers.SlugRelatedField(
read_only=True,
many=True,
source='related_cta_variation_ctas',
slug_field='variation'
)
target_content = serializers.SlugRelatedField(
read_only=True,
many=True,
source='related_cta_variation_ctas',
slug_field='target_content'
)
class Meta:
model = CTA
fields = ('id','css_path', 'source_content', 'category','subcategory', 'ctype', 'target_content','variation')
The serializer is invoked as follows:
PageCTASerializerForVariation(page.related_page_ctas.get(CTA Model Instance).data
When the API is used from the browser the serialized instance is a follows:
{'category': "Title", 'subcategory': "Page Title",
'target_content': u'asjbsnak', 'source_content': u'hsdvhas',
'ctype': 'HTML', 'css_path': u'asdjbjsdansdas>sakdbja>dafhds',
'id': 1}
But when I call it through the test case,the serialized instance is as follows:
{'category': u"[u'Title']", 'subcategory': u"[u'Page Title']",
'target_content': u'asjbsnak', 'source_content': u'hsdvhas',
'ctype': 'HTML', 'css_path': u'asdjbjsdansdas>sakdbja>dafhds',
'id': 1}
Notice the category and subcategory fields in the serialized instances above.
In the normal API call they are output as a normal string(this is the expected output),whereas in the test case call they are output as a list with extra characters(unexpected).
This is causing my test case assertions to fail.
Please Help!!

Django Rest Framework how to save a model with Related Field based on ID

I'm kind of new to DRF. I have Record model that looks like this:
class Records(models.Model):
owner = models.ForeignKey(User, null=True)
activity = models.ForeignKey(Activity, null=True)
time_start = models.DateTimeField(null=True)
time_end = models.DateTimeField(null=True)
...
The RecordSerializer is this one:
class RecordSerializer(serializers.ModelSerializer):
now = datetime.today()
owner = serializers.Field(source='owner.username')
time_start = serializers.DateTimeField(source='now')
class Meta:
model = Records
fields = ("owner", "activity", "time_start")
And this is the view:
class StartApiView(generics.CreateAPIView):
model = Records
serializer_class = RecordSerializer
def pre_save(self, obj):
obj.owner = self.request.user
The POST request is sent from Backbone and it includes a field with the activity id, for example "{activity:12}". What should I do if I want the view to save the Record and set the activity to the Activity with the id of 12?
The accepted answer was true for DRF v2.x but is no longer for newer (3.x) versions, as it would raise this AssertionError:
AssertionError: Relational field must provide a queryset argument, or set read_only=True.
For newer versions, just add the queryset argument to it:
class RecordSerializer(serializers.ModelSerializer):
activity = serializers.PrimaryKeyRelatedField(queryset=Activity.objects.all())
// [...]
Django REST Framework provides a PrimaryKeyRelatedField for exactly this use case.
class RecordSerializer(serializers.ModelSerializer):
activity = serializers.PrimaryKeyRelatedField()
owner = serializers.CharField(read_only=True, source='owner.username')
time_start = serializers.DateTimeField(source='now')
class Meta:
model = Records
fields = ("owner", "activity", "time_start")
This will produce output similar to what you are looking for, and it will accept the id of the activity when you want to update it.