What is the pk field in the Serializer class in Django-REST-framework?
I assume that it is the primary key, but is the name 'pk' a reserved term?
How does the Serializer class know that is to be the primary key of the Snippet model?
I see no field in the Snippet model named 'pk'.
class SnippetSerializer(serializers.Serializer):
pk = serializers.Field() # Note: `Field` is an untyped read-only field.
title = serializers.CharField(required=False,
max_length=100)
code = serializers.CharField(widget=widgets.Textarea,
max_length=100000)
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES,
default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES,
default='friendly')
....class SnippetSeralizer continues
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES,
default='python',
max_length=100)
style = models.CharField(choices=STYLE_CHOICES,
default='friendly',
max_length=100)
class Meta:
ordering = ('created',)
pk is a property that lives on the base Model class in django.db.models:
class Model(object):
...
pk = property(_get_pk_val, _set_pk_val)
...
which is used to identify the primary key for the model. I haven't used Django-REST, but they probably just map that to the field on the model.
And if you are using generic views in Django REST Framework, and you would like to use a different name for the pk field — say id, you can set lookup_field in your view to id.
Related
I am Django rest framework to return the list of objects who do not have a foreign key in another table. what queryset should I write to get those objects.
models.py
class Event(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=100,default='')
description = models.TextField(blank=True,default='', max_length=1000)
link = models.URLField(null=True)
image = models.ImageField(null=True, blank=True)
organizer = models.CharField(max_length=100, default='')
timings = models.DateTimeField(default=None)
cost = models.IntegerField(default=1,null=True,blank=True)
def __str__(self):
return self.title
class Featured(models.Model):
event = models.ForeignKey(Event, null=True ,on_delete=models.PROTECT, related_name="event")
def __str__(self):
return self.event.title
class Meta:
verbose_name_plural = 'Featured'
views.py
class Upcoming2EventsViewSet(mixins.RetrieveModelMixin,mixins.ListModelMixin,viewsets.GenericViewSet):
serializer_class = Upcoming2Events
def get_queryset(self):
featured_events = Featured.objects.all().values_list('id')
return Event.objects.filter(id__in=featured_events)
# return Event.objects.exclude(id__in=featured_events.event.id)
# # return Event.objects.exclude(id__in = [featured_events.id])
serializers.py
class Upcoming2Events(serializers.ModelSerializer):
id = serializers.CharField(source='event.id')
title = serializers.CharField(source='event.title')
timings = serializers.DateTimeField(source='event.timings')
organizer = serializers.CharField(source='event.organizer')
class Meta:
model = Featured
fields = ['id','title','organizer','timings']
Error
Got AttributeError when attempting to get a value for field `id` on serializer `Upcoming2Events`.
The serializer field might be named incorrectly and not match any attribute or key on the `Event` instance.
Original exception text was: 'RelatedManager' object has no attribute 'id'.
Can you tell me what queryset should I write to get the only objects which are not present in the table Featured?
Also, what should I do to get only the upcoming 2 events from the Event table which are not present in the Featured table?
Note I am not supposed to use any flag value, can you provide some other solutions?
Based on the Informations you wrote here, i would suggest using a flag to determine a featured event. A second Model is useful if you want to provide more Informations on this specific for a featured event
like this:
class Event(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=100,default='')
description = models.TextField(blank=True,default='', max_length=1000)
link = models.URLField(null=True)
image = models.ImageField(null=True, blank=True)
organizer = models.CharField(max_length=100, default='')
timings = models.DateTimeField(default=None)
cost = models.IntegerField(default=1,null=True,blank=True)
featured = models.BooleanField(default=False)
so you can directly use querysets to get what you want:
Event.objects.exclude(featured=True)
Event.objects.exclude(featured=True).order_by('-timings')[:2]
I would use ModelViewsets directly, hence you will use your model here.
views and serializers would look like this:
views.py
class Upcoming2EventsViewSet(viewesets.ReadyOnlyModelViewSet):
serializer_class = EventSerializer
queryset = Event.objects.exclude(featured=True).order_by('-timings')[:2]
serializers.py
class EventSerializer(serializers.ModelSerilizer):
class Meta:
model = Event
fields = ['id', 'title', 'organizer', 'timings']
As improvement i would provide filters instead of setting up different ViewSets for just filtering querysets.
How do I use Serializer for something like This
class Language(BaseModel):
name = models.CharField(null=False, blank=False, unique=True, max_length=150)
class Helper(BaseModel):
languages = models.ManyToManyField('Language', blank=True, through="HelperLanguage")
class HelperLanguage(BaseModel):
helper = models.ForeignKey('Helper', on_delete=models.CASCADE)
language = models.ForeignKey('Language', on_delete=models.CASCADE)
read = models.BooleanField()
write = models.BooleanField()
speak = models.BooleanField(default=True)
class LanguageSerializer(ModelSerializer):
class Meta:
model = Language
fields = ["id", "name"]
class HelperLanguageSerializer(ModelSerializer):
language = LanguageSerializer(read_only=True)
class Meta:
model = HelperLanguage
fields = ["id", "language", "read", "write", "speak"]
class HelperPublicSerializer(ModelSerializer):
languages = HelperLanguageSerializer(read_only=True, many=True)
class Meta:
model = Helper
fields = ['id', 'languages']
while using HelperPublicSerialiser for list view I am getting error
Got AttributeError when attempting to get a value for field read on serializer HelperLanguageSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Language instance.
I do understand the problem but couldn't find any solution probably not using membership model right way.
Looking for solution of this problem I encountered some similar threads, but referring to older versions of Django/DRF and thus not working in my case.
There are these two models:
class CsdModel(models.Model):
model_id = models.CharField("Item ID", max_length=8, primary_key=True)
name = models.CharField("Item Name", max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class CsdListing(models.Model):
model_id = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_id')
name = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_name')
(...)
EDIT: Serializers are defined this way:
class CsdModelSerializer(serializers.ModelSerializer):
model_id = serializers.RegexField(regex='^\w{2}\d{3}$', allow_blank=False)
name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)
class Meta:
model = CsdModel
fields = '__all__'
class CsdListingSerializer(serializers.ModelSerializer):
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
def validate_session_id(self, value):
(...)
class Meta:
model = CsdListing
fields = '__all__'
What I'd like to see, is model_id and name from CsdModel displayed inside a form created based on CsdListing model. But instead, the ID is duplicated:
How should I rebuild the model(s) to have both ID and name displayed in the form?
You should have only one foreign key. But the listing serializer should then reference the model as a nested serializer.
class CsdListing(models.Model):
model = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='listing')
class CsdListingSerializer(serializers.ModelSerializer):
model = CsdModelSerializer()
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
I have the following models in Django:
class campaign(models.Model):
start_date = models.DateField('Start Date')
end_date = models.DateField('End Date')
description = models.CharField(max_length=500)
name = models.CharField(max_length=200)
active_start_time = models.TimeField()
active_end_time = models.TimeField()
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
client_id = models.ForeignKey('client',on_delete=models.PROTECT)
def __unicode__(self):
return u'%d | %s | %s' % (self.id,self.name, self.description)
class campaign_product(models.Model):
product_id = models.ForeignKey('product',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
campaign_id = models.ForeignKey('campaign',on_delete=models.PROTECT)
class product(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=500)
sku = models.CharField(max_length=200,blank=True,null=True)
retail_price = models.DecimalField(decimal_places=2,max_digits=11)
discount_price = ((1,'Yes'),(0,'No'))
discounted_price = models.DecimalField(decimal_places=2,max_digits=11,blank=True,null=True)
category_id = models.ForeignKey('category',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
def __unicode__(self):
return u'%d | %s' % (self.id, self.name)
I also have the following serializer:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id')
And the following view set behavior in the urls.py file:
class campaignProductViewSet(viewsets.ModelViewSet):
queryset = campaign_product.objects.filter(campaign_id__start_date__lte=datetime.now(),campaign_id__end_date__gte=datetime.now(),campaign_id__active__exact=True)
serializer_class = campaignProductSerializer
My problem is I need to include the name field from the products model in my query results when for instance a request is made on http://127.0.0.1:8000/campaign_product/1/. Currenly this request returns only the product_id and the campaign_id. I tried making the serializer as follows:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product.name')
But then the service returns the following error:
Field name `product.name` is not valid for model `campaign_product`.
I event tried using product__name with and without quotes. Without quotes it tells me that there is no such variable, and with quotes it gives the is not valid for model error similar to the above. Heeelp! Getting this extra field is proving to be a pain :-(
What you want will need to look something more like this:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
product_name = serializers.CharField(source='product_id.name')
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product_name')
P.S. As an unrelated side note, it is generally a convention in Python code to name classes with CamelCase, such as Campaign, CampaignProduct, Product, and CampaignProductSerializer.
Edit: P.P.S. Originally, I had put written the product_name field with source='product.name'. This was actually due to me looking at the code too quickly and making assumptions based on Django conventions. Typically, with a Django ForeignKey, you would name the ForeignKey field after the model you are linking to, rather than explicitly naming it with _id. For example, the CampaignProduct model would typically be written with product = ForeignKey(...) and campaign = ForeignKey(...). In the background, Django will actually use product_id and campaign_id as the database field names. You also have access to those names on your model instances. But the product and campaign variables on your model instances actually return the objects which you are referring to. Hopefully that all makes sense.
I the following in the models.py:
class Item(models.Model):
date = models.DateField(_('date'), blank=True, null=True)
description = models.CharField(_('description'), max_length=255)
content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
object_id = models.PositiveIntegerField(_('object id'), db_index=True)
object = generic.GenericForeignKey('content_type', 'object_id')
class ItemAccountAmountRef(Item):
""" Items of which a Quote or an Invoice exists. """
amount = models.DecimalField(max_digits=10, decimal_places=2)
reference = models.CharField(max_length=200)
debit_account = models.ForeignKey(Account, related_name='receivables_receipt_debit_account')
credit_account = models.ForeignKey(Account, related_name='receivables_receipt_credit_account')
class PaymentItem(ItemAccountAmountRef):
pass
class Payment(models.Model):
invoice = models.ManyToManyField(Invoice, null=True, blank=True)
date = models.DateField('date')
attachments = generic.GenericRelation(Attachment)
site = models.ForeignKey(Site, related_name='payment_site', null=True, blank=True
items = generic.GenericRelation(PaymentItem)
in the admin.py:
class PaymentItemInline(generic.GenericTabularInline):
model = PaymentItem
form = PaymentItemForm
class PaymentAdmin(admin.ModelAdmin):
inlines = [PaymentItemInline]
in forms.py:
class PaymentItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PaymentItemForm, self).__init__(*args, **kwargs)
self.fields['credit_account'].label = "Bank Account"
In the PaymentItemInline the label is not changing. I have tried changing other attributes e.g. class which work. If I run through the init in debug mode I can see that the label variable is changing however when the form is rendered the field is still labelled credit account. Any suggestions?
You're 98% of the way there. Instead of trying to futz with the form field in __init__, just redefine it in your ModelForm. If you name it the same thing, django will be able to figure out that it is supposed to validate & save to the ForeignKey field. You can use the same formula to change a Field or Widget completely for a given field in a ModelForm.
You can find the default form field types for each model field type here: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#field-types
class PaymentItemForm(forms.ModelForm):
credit_account = forms.ModelChoiceField(label="Bank Account", queryset=Account.objects.all())
That's it. No need to override any functions at all : )
Incidentally, the docs for this field are here: https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield