Display full names in Form ChoiceField but saving ID's - django

I have model Person - from another database
I copied all person_id to custom_id.
models.py
class Employee(models.Model):
custom_id = models.CharField(max_length=20, unique=True)
#property
def person(self):
return Person.objects.get(person_id='%s' % self.custom_id)
def __str__(self):
return '%s' % self.custom_id
class Task(models.Model):
employee = models.ManyToManyField(Employee, blank=True, null=True)
task = models.CharField(max_length=100)
comment = models.CharField(max_length=200)
def __str__(self):
return '%s' % self.task
I add my method person() to Employee which allow me to access other objects model in another database:
So basically when I type this in shell:
Employee.objects.get(custom_id='123').person.full_name
u'Adam Dylan'
I have a ModelForm which use ModelMultipleChoiceField
forms.py
class TaskCreateForm(forms.ModelForm):
employee = forms.ModelMultipleChoiceField(queryset=Employee.objects.all())
class Meta:
model = Task
But Employee.objects.all() returns bunch of custom_id's.
What I want is to show in form "Employee(..).person.full_name" but saving only custom_id's.

I am not sure why you think the answer I gave to your other question does not work here. Did you try the following? If it does not work, how exactly does it fail?
class EmployeeMultipleChoiceField(ModelMultipleChoiceField):
def label_from_instance(self, obj):
return obj.person.full_name
class TaskCreateForm(forms.ModelForm):
employee = EmployeeMultipleChoiceField(queryset=Employee.objects.all())
class Meta:
model = Task

Related

How to create or update more than one objects of different models from one end point

How to update more than one object of different model types from one end point. I tried it many ways but i still fails.I tried through nested serializer and create method, but it is still not working
class Student(models.Model):
name = models.CharField(max_length=300)
sex = models.CharField(choices=SEX_CHOICES,max_length=255,
null=True)
Category = models.CharField(max_length=100, null=True)
def __str__(self):
return self.name
class Registration(models.Model):
registration_no = models.CharField(max_length=255,
unique=True)
student = models.OneToOneField(Student,
on_delete= models.CASCADE, related_name='registrations')
def __str__(self):
return self.registration_no
class RegistrationSerializer(serializers.ModelSerializer):
class Meta:
model = Registration
fields = '__all__'
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = '__all__'
class StudentDataMigrateSerializer(serializers.Serializer):
student = StudentSerializer()
registation = RegistrationSerializer()
In Django Rest Framework by default the nested serializers are read only. To have a writable nested serializer you need to implement create() and/or update() methods.
Take a look at the official documentation https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
class StudentDataMigrateSerializer(serializers.Serializer):
student = StudentSerializer()
registation = RegistrationSerializer()
def create(self, validated_data):
# save the data

DRF ListSerializer and ListField

I use django rest in my project and until now for list of objects I used ListSerializer, when I needed to have min length and max length of list I googled and reached to ListField.
Before that my code worked fined without any error and misbehavior. Now I use ListField for my list field serializer, But I didn't get when to use ListSerializer? Can someone explain the difference between ListSerializer and FieldSerializer?
My sample code with ListSerializer:
tags = serializers.ListSerializer(child=serializers.CharField(allow_blank=False), required=False)
My sample code with ListField:
open_hour = serializers.ListField(child=serializers.DictField(), max_length=7, min_length=7)
Disclaimer: This answer is not complete
Can someone explain the difference between ListSerializer and
FieldSerializer?
I assume the question is difference between serializers.ListSerializer and serializers.ListField
Suppose we have two models as
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
def __str__(self):
return f'{self.name} : {self.artist}'
and serializer as
class AlbumSerializer(serializers.ModelSerializer):
artist = serializers.StringRelatedField()
class Meta:
fields = '__all__'
model = Album
class MusicianSerializer(serializers.ModelSerializer):
AlbumSerializer(many=True, source='album_set')
class Meta:
fields = '__all__'
model = Musician
ListSerializer
As stated in official DRF doc
When a serializer is instantiated and many=True is passed, a
ListSerializer instance will be created. The serializer class then
becomes a child of the parent ListSerializer
For example, we could re-write the MusicianSerializer with ListSerializer as
class MusicianSerializer(serializers.ModelSerializer):
albums = serializers.ListSerializer(child=AlbumSerializer(), source='album_set')
class Meta:
fields = '__all__'
model = Musician
it would produce the results same as before. But, if we are trying to use ListField instead of ListSerializer It will raise an error
'RelatedManager' object is not iterable
When I checked the source code, I found that both ListSerializer and ListField are inherited from the same class (parent and grand parent are same)
I ran into this same problem and I believe I found a solution!
The trick is you need to create a new Serializer that inherits the ListSerializer class and override the to_representation() method to output your desired format.
If you look at the DRF source code for ListSerializer you can see the default to_representation() method looks like the following...
def to_representation(self, data):
"""
List of object instances -> List of dicts of primitive datatypes.
"""
# Dealing with nested relationships, data can be a Manager,
# so, first get a queryset from the Manager if needed
iterable = data.all() if isinstance(data, models.Manager) else data
return [
self.child.to_representation(item) for item in iterable
]
Example
models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
def __str__(self):
return f'{self.name} : {self.artist}'
serializers
class AlbumSerializer(serializers.ModelSerializer):
artist = serializers.StringRelatedField()
class Meta:
fields = '__all__'
model = Album
class AlbumKeyValueSerializer(serializers.ListSerializer):
def to_representation(self, data):
reaction_count_set = {}
for item in data.all():
reaction_count_set[item.name] = item.artist
return reaction_count_set
class MusicianSerializer(serializers.ModelSerializer):
AlbumKeyValueSerializer(child=AlbumSerializer(), source='album_set')
class Meta:
fields = '__all__'
model = Musician

Local field 'created_at' in class 'BandRating' clashes with field of the same name from base class 'Rating'

I've recently tried to create two separate models inheriting from Rating but upon migration I get the error mentioned in the title. I assume this is due to rogue migrations as it seems that there should be no clashes in my code? I initially had Rating which had two optional fields, Venue or Band but I feel this is better structure.
For future reference, what would be the ideal way to do this without running into this kind of issue?
class Rating(models.Model, Activity):
created_at = models.DateTimeField(auto_now_add=True, null=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
rating = models.IntegerField(default=0)
description = models.CharField(max_length=200, null=True)
class Meta:
abstract = True
def get_author(self):
if self.author is None:
return "Anonymous"
else:
return self.author
#property
def activity_actor_attr(self):
return self.author
class BandRating(Rating):
band = models.ForeignKey(Band)
def __str__(self):
return str(self.band) + " rating"
class VenueRating(Rating):
venue = models.ForeignKey(Venue)
def __str__(self):
return str(self.venue) + " rating"
It seems that you are trying to "hide" the Rating.created_at field which is declared in the Activity model from which you inherit - that's not possible when inheriting from a non-abstract model.
More info here:
https://docs.djangoproject.com/en/dev/topics/db/models/#field-name-hiding-is-not-permitted

Nested objects save in DRF

Models:
class Owner(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
class SomeThing(models.Model):
own_id = models.IntegerField(unique=True)
description = models.CharField(max_length=255, blank=True)
owner = models.ForeignKey(Owner, blank=True, null=True)
def __unicode__(self):
return self.description
Serializers:
class OwnerNameField(serializers.RelatedField):
def to_internal_value(self, data):
pass
def to_representation(self, value):
return value.name
def get_queryset(self):
queryset = self.queryset
if isinstance(queryset, (QuerySet, Manager)):
queryset = queryset.all()
lista = [Owner(name="------------")]
lista.extend(queryset)
return lista
class OwnerSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Owner
fields = ('name', 'id')
class ThingSerializer(serializers.ModelSerializer):
owner = OwnerNameField(queryset=Owner.objects.all())
class Meta:
model = SomeThing
fields = ('own_id', 'description', 'owner')
Basically it works as intended. But when i add some fields to Owner class i would like to see all these fields in output of ThingSerializer (and be able to parse them - string doesn't suit here). I could change field owner to owner = OwnerSerializer() which gives me what i need. But when i want to add SomeThing object (tested in API browser) i also need add new Owner object - and i don't want it, i want use existing Owner object. How can i achieve it?
Finally i got it. This question describes exactly my problem and provided answers work as a charm!

django-piston: how to get values of a many to many field?

I have a model with ManyToManyField to another model. I would like to get all the info on a particular record (including the related info from other models) return by JSON.
How to get django-piston to display those values? I would be happy with just primary keys.
Or can you suggest another option ?
I may be wrong, but this should do it:
class PersonHandler(BaseHandler):
model = Person
fields = ('id', ('friends', ('id', 'name')), 'name')
def read(self, request):
return Person.objects.filter(...)
You need to define a classmethod on the handler that returns the many-to-many data, I don't believe Piston does this automatically.
class MyHandler(BaseHandler):
model = MyModel
fields = ('myfield', 'mymanytomanyfield')
#classmethod
def mymanytomanyfield(cls, myinstance):
return myinstance.mymanytomanyfield.all()
My code:
Models:
class Tag(models.Model):
"""docstring for Tags"""
tag_name = models.CharField(max_length=20, blank=True)
create_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.tag_name
class Author(models.Model):
"""docstring for Author"""
name = models.CharField(max_length=30)
email = models.EmailField(blank=True)
website = models.URLField(blank=True)
def __unicode__(self):
return u'%s' % (self.name)
class Blog(models.Model):
"""docstring for Blogs"""
caption = models.CharField(max_length=50)
author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag, blank=True)
content = models.TextField()
publish_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s %s %s' % (self.caption, self.author, self.publish_time)
Handle:
class BlogAndTagsHandler(BaseHandler):
allowed_methods = ('GET',)
model = Blog
fields = ('id' 'caption', 'author',('tags',('id', 'tag_name')), 'content', 'publish_time', 'update_time')
def read(self, request, _id=None):
"""
Returns a single post if `blogpost_id` is given,
otherwise a subset.
"""
base = Blog.objects
if _id:
return base.get(id=_id)
else:
return base.all() # Or base.filter(...)
Works petty good.