How can i add custom fields to ModelSerializer? - django-models

i have a PurchaseDetail model like
class PurchaseDetail(NowTimestampAndUserModel):
purchase = models.ForeignKey(PurchaseMaster, default=False, related_name="purchase_details", on_delete=models.CASCADE)
item = models.ForeignKey(Item, default=False, on_delete=models.CASCADE)
item_category = models.ForeignKey(ItemCategory, default=False, on_delete=models.CASCADE)
purchase_cost = models.DecimalField(max_digits=19, decimal_places=2, default=0.0)
sale_cost = models.DecimalField(max_digits=19, decimal_places=2, default=0.0)
qty = models.FloatField(help_text="Purchase quantity")
ref_purchase_order_detail = models.PositiveIntegerField(default=0)
ref_purchase_detail = models.PositiveIntegerField(default=0)
and i have "Item" model and "item_category" models as
ITEM
class ItemCategory(NowTimestampAndUserModel):
name = models.CharField(max_length=50, unique=True, help_text="Category name should be max. of 50 characters")
code = models.CharField(max_length=10, unique=True, help_text="Province name should be max. of 10 characters")
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
ITEM_CATEGORY
class ItemCategoryHist(NowTimestampAndUserModel):
name = models.CharField(max_length=50, help_text="Category name should be max. of 50 characters")
code = models.CharField(max_length=10, help_text="Province name should be max. of 10 characters")
def __str__(self):
return self.name
i want to make serializer class as
class StockSerializer(serializers.ModelSerializer):
item_name = Field(source='get_item_name')
class Meta:
model = PurchaseDetail
# some fields of PurchaseDetail
fields = ['id', 'item_name ', 'item_category']
def get_item_name(self, obj):
item_id = obj.item
item_obj = Item.objects.get(pk=item_id)
name = item_obj.name
return name
I want to serialize item name field only with Purchase Detai. How ca i do it?

What you need are Serializer Method Fields: https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
The field "item_name" can be a SerializerMethodField, allowing you to have custom code to generate the value for that field.

Related

Django Post request for many to many field ValueError

I am working on a post request in which the user chooses from a list of tags and makes combinations of tags. The combination of tags should then be posted. Nothing should get changed in the Tag table.
These are the models:
models.py
class Tag(models.Model):
name = models.CharField(max_length=256)
language = models.CharField(max_length=256)
objects = models.Manager()
def __str__(self):
"""Return a human readable representation of the model instance."""
return self.name or ''
#property
def tags(self):
tags = self.tagging.values('tag')
return tags.values('tag_id', 'tag__name', 'tag__language')
class Combination(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, null=True)
resource = models.ForeignKey(Resource, on_delete=models.CASCADE, null=True)
tag_id = models.ManyToManyField(Tag, null=True)
created = models.DateTimeField(editable=False)
score = models.PositiveIntegerField(default=0)
objects = models.Manager()
def __str__(self):
return str(self.tag_id) or ''
This is the serializer for Combination.
serializers.py
class CombinationSerializer(serializers.ModelSerializer):
tag_id = TagWithIdSerializer(many=True, required=False, write_only=False)
resource_id = serializers.PrimaryKeyRelatedField(queryset=Resource.objects.all(),
required=True,
source='resource',
write_only=False)
gameround_id = serializers.PrimaryKeyRelatedField(queryset=Gameround.objects.all(),
required=False,
source='gameround',
write_only=False)
user_id = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all(),
required=False,
source='user',
write_only=False)
class Meta:
model = Combination
depth = 1
fields = ('id', 'user_id', 'gameround_id', 'resource_id', 'tag_id', 'created', 'score')
def create(self, validated_data):
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
score = 0
tag_data = validated_data.pop('tag_id', None)
combination = Combination(
user=user,
gameround=validated_data.get("gameround"),
resource=validated_data.get("resource"),
created=datetime.now(),
score=score
)
combination.save()
for tag_object in tag_data[0]:
combination.tag_id.add(tag_object)
return combination
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['tag_id'] = TagWithIdSerializer(instance.tag_id.all(), many=True).data
return rep
I have tried posting the following JSON object to the database:
{
"gameround_id": 2015685170,
"resource_id": 327888,
"tag_id": [{"id": 2014077506, "name": "corwn","language": "en"}]
}
I am getting a ValueError: Field 'id' expected a number but got 'name'.
How can I fix this issue?
you need to provide tag id for each tag not all tag data,
Try like this
{
"gameround_id": 2015685170,
"resource_id": 327888,
"tag_id": [2014077506,2014077507]
}

How do i access another column from related table other than the foreign key, when creating an API view

Im using django for a web app and i am creating REST API views. Is there a way i can access two tables in one view? If not, how can can i retrieve a non-foreign key column from a related record. The below code is retrieving a vase record based on a URL parameter. I want to access the artistName which is stored in artist table (a one-to-many with Vase table), not artist_id which is stored in Vase
class FilterVases(generics.ListAPIView):
serializer_class = VaseSerializer
def get_queryset(self):
queryset = Vase.objects.all()
artist_id = self.request.query_params.get('artist_id')
if artist_id is not None:
queryset = queryset.filter(artist_id=artist_id)
vaseID = self.request.query_params.get('vaseID')
if vaseID is not None:
queryset = queryset.filter(vaseID=vaseID)
return queryset
edited to add
This is models for Artist and Vase:
class Artist(models.Model) :
artistID = models.CharField(max_length=10)
artistName = models.CharField(max_length=100)
class Vase(models.Model):
vaseID = models.CharField(max_length=10)
vaseRef = models.CharField(max_length=255,blank=True,null=True)
inscription = models.CharField(max_length=255,blank=True,null=True)
fabric = models.CharField(max_length=100, blank=True,null=True)
subject = models.CharField(max_length=255,blank=True,null=True)
technique = models.CharField(max_length=100,blank=True,null=True)
height = models.FloatField(max_length=100,blank=True,null=True)
diameter = models.FloatField(max_length=100,blank=True,null=True)
shape = models.ForeignKey(Shape, on_delete=models.CASCADE)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
provenance = models.ForeignKey(Provenance, on_delete=models.CASCADE)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
In the Vase model add this:
def artist_name(self):
return self.artist.artistName
Hence, it will look like:
class Vase(models.Model):
vaseID = models.CharField(max_length=10)
vaseRef = models.CharField(max_length=255,blank=True,null=True)
inscription = models.CharField(max_length=255,blank=True,null=True)
fabric = models.CharField(max_length=100, blank=True,null=True)
subject = models.CharField(max_length=255,blank=True,null=True)
technique = models.CharField(max_length=100,blank=True,null=True)
height = models.FloatField(max_length=100,blank=True,null=True)
diameter = models.FloatField(max_length=100,blank=True,null=True)
shape = models.ForeignKey(Shape, on_delete=models.CASCADE)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
provenance = models.ForeignKey(Provenance, on_delete=models.CASCADE)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
def artist_name(self):
return self.artist.artistName
In the VaseSerializer add the 'artist_name' to the fields Meta.
If you want to add this custom fields to all Vase Model fields, refer to this topic Django Rest framework, how to include '__all__' fields and a related field in ModelSerializer ?
class VaseSerializer(serializers.ModelSerializer):
class Meta:
model = models.Vase
fields = '__all__'
extra_fields = ['artist_name']
def get_field_names(self, declared_fields, info):
expanded_fields = super(VaseSerializer, self).get_field_names(
declared_fields, info)
if getattr(self.Meta, 'extra_fields', None):
return expanded_fields + self.Meta.extra_fields
else:
return expanded_fields
Below should your view:
class FilterVases(generics.ListAPIView):
serializer_class = VaseSerializer
def get_queryset(self):
queryset = Vase.objects.all()
query_artist = self.request.query_params.get('artist_name')
if query_artist is not None:
try:
artist = Artist.objects.get(artistName=query_artist)
queryset = queryset.filter(artist=artist)
except:
pass
vaseID = self.request.query_params.get('vaseID')
if vaseID is not None:
queryset = queryset.filter(vaseID=vaseID)
return queryset

Foreign key return object instead of integer

in "models.py"
class Run(models.Model):
number = models.IntegerField(choices=((int(x), x) for x in range(1, 50)))
type = models.CharField(max_length=10, choices=((str(x), x) for x in ['bit', 'casing']))
run_date = models.DateTimeField(default=timezone.now)
well = models.ForeignKey(Well, related_name='run', on_delete=models.CASCADE)
created_by = models.ForeignKey(User, related_name='run_created_by', null=True, on_delete=models.SET_NULL)
description = models.CharField(max_length=255)
class Meta:
verbose_name = "Run Information"
verbose_name_plural = "Run Information"
def __int__(self):
return self.number
class BitRun(models.Model):
number = models.PositiveSmallIntegerField(choices=((int(x), x) for x in range(1, 50)))
od = models.DecimalField(max_digits=5, decimal_places=3)
inner_diameter = models.DecimalField(max_digits=5, decimal_places=3)
tfa = models.DecimalField(max_digits=5, decimal_places=3)
bottom_depth = models.DecimalField(max_digits=7, decimal_places=2)
top_depth = models.DecimalField(max_digits=7, decimal_places=2)
run = models.ForeignKey(Run, related_name='bit_number', on_delete=models.CASCADE)
well = models.ForeignKey(Well, related_name='well_name', on_delete=models.CASCADE)
class Meta:
verbose_name = "Bit Record"
verbose_name_plural = "Bit Records"
in "admin.py"
class BitRunAdmin(admin.ModelAdmin):
ordering = ('number',)
list_display = ('well', 'run', 'od', 'tfa')
list_filter = ['well']
search_fields = ['well']
I see run object(1) instead of the actual fields value.
Can someone please point what i am doing wrong in the code?
Thank you
add __str__ representation for each model.
def __str__(self):
rerurn f"{self.field_name}"
field_name must be the name of field you want to see.

How can I access my models in django admin?

I am working on an AlumniTracker in django and till now I have created forms for user sign up and some additional information.
Even after saving the additional information form I am not able to access it in django-admin.
I am adding my models, views and forms file here.
views.py
def student_profile(request):
if request.method == 'POST':
form = StudentDetailForm(request.POST)
if form.is_valid:
student_form = form.save(commit=False)
student_form.user = request.user
student_form.save()
return redirect(reverse('homepage'))
else:
form = StudentDetailForm()
return render(request, 'authentication/student_profile.html', {'form':form})
def alumni_profile(request):
if request.method == 'POST':
form = AlumniDetailForm(request.POST)
if form.is_valid:
alumni_form = form.save(commit=False)
alumni_form.user = request.user
alumni_form.save()
return redirect(reverse('homepage'))
else:
form = AlumniDetailForm()
return render(request, 'authentication/student_profile.html', {'form':form})
forms.py
class StudentDetailForm(ModelForm):
class Meta:
model = StudentDetail
fields = ['first_name', 'last_name', 'contact_no', 'birth_date', 'course', 'session_start', 'session_end']
class AlumniDetailForm(ModelForm):
class Meta:
model = AlumniDetail
exclude = ['user']
models.py
class CustomUser(AbstractUser):
profile = models.CharField(max_length=10,choices=PROFILE_CHOICES, default='student')
def __str__(self):
return self.username
class StudentDetail(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
contact_no = models.IntegerField()
birth_date = models.DateField()
course = models.CharField(max_length=50)
session_start = models.IntegerField()
session_end = models.IntegerField()
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class AlumniDetail(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
contact_no = models.IntegerField()
birth_date = models.DateField()
course = models.CharField(max_length=50)
session_start = models.IntegerField()
session_end = models.IntegerField()
company = models.CharField(max_length=60)
open and edit admin.py
and register your models by adding code below admin import
from .models import CustomUser,StudentDetail,AlumniDetail
admin.site.register(CustomUser)
admin.site.register(StudentDetail)
admin.site.register(AlumniDetail)

How to create model objects via ModelForm ForeignKey?

I have a model for Classroom and Student as shown below
class Classroom(models.Model):
COURSE_NAME = (
('MA8', 'Math 8'),
('SC10', 'Science 10'),
('PH11', 'Physics 11'),
('PH12', 'Physics 12'),
)
BLOCK_NUMBER = (
('11', 'Block 1-1'),
('12', 'Block 1-2'),
('13', 'Block 1-3'),
('14', 'Block 1-4'),
('21', 'Block 2-1'),
('22', 'Block 2-2'),
('23', 'Block 2-3'),
('24', 'Block 2-4'),
)
class_list = models.TextField()
course_name = models.CharField(max_length=20, choices=COURSE_NAME)
course_block = models.CharField(max_length=10, choices=BLOCK_NUMBER)
class Student(models.Model):
classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE)
nickname = models.CharField(default='JohnS', max_length=31)
attend = models.BooleanField(default=True)
I created a form for Classroom.class_list and TextField is where the user copy/pastes a list of names. I want to then parse the class_list and save each individual name as nickname. I first tried the following but this doesn't seem to save the Student objects.
forms.py
class ClassroomForm(ModelForm):
class Meta:
model = Classroom
fields = ['course_name', 'course_block','class_list']
views.py
class ClassroomCreateView(CreateView):
model = Classroom
form_class = ClassroomForm
def form_valid(self, form):
classroom = form.save(commit=False)
s = Student()
for line in classroom.class_list:
s.nickname = line
s.save()
classroom.save()
return super(ClassroomCreateView, self).form_valid(form)
def get_success_url(self):
return reverse('classroom:submitted')
I also tried creating StudentForm which allows a user to choose course_name and course_block (which corresponds to a particular class_list). The form or view would then create the individual Student objects and display them. I read about ModelChoiceField but I can't figure out how to implement this.
How and where do I (auto) create Students objects from a ForeignKey field?
I solved my question with the help of this answer. Here is the modified code I used for models.py. My view is just a standard CreateView from the ModelForm.
class Classroom(models.Model):
... dictionary stuff ..
class_list = models.TextField()
course_name = models.CharField(max_length=20, choices=COURSE_NAME)
course_block = models.CharField(max_length=10, choices=BLOCK_NUMBER)
group_size = models.IntegerField(default=3)
def __str__(self):
return self.get_course_block_display()
def save(self, *args, **kwargs):
super(Classroom, self).save(*args, **kwargs)
# overrides the default save function to parse the class list
studentList = []
studentList = self.class_list.split('\n')
for line in studentList:
line = line.strip('\r')
s = Student.objects.create(nickname = line, classroom = self)

Resources