Copying only non-relation attributes django/python
Question:
I am copying a model object to another, but I want that it doesn’t copy the relations
For example, assume you have a model like this:
class Dish(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=500)
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1)
def __str__(self):
return self.name
Then I do:
my_dish = Dish.objects.get(pk=dish.id)
serializer = Dish_Serializer(my_dish)
my_new_object = serializer.data
I want my_new_object
to include only those attributes that are not relations, in this case, name and description.
How do I do that without accessing name and description directly?
Answers:
I assume in your serializer you don’t want to explicitly define which field to serialize. Otherwise you could do the following:
class Dish_Serializer(serializers.ModelSerializer):
class Meta:
model = Dish
fields = ['id','name', 'description']
You probably can define these fields dynamically:
fields = [f.name for f in Dish._meta.concrete_fields]
or
fields = [f.name for f in Dish._meta.fields if not isinstance(f,ForeignKey)]
Ultimately, you want my_new_object in dictionary format and as per condition pk will give you only one object of dish.
So, you can do this instead :
my_new_object = Dish.objects.filter(pk=dish.id).values("name", "description")[0]
It will give you exact what you want, just declare the fields you need in values as an attribute fields.
You can remove a field from your serializer using .fields.pop(field_name)
method like the below example According that I took from Dynamically modifying fields
[drf-docs]:
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super().__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` >argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
Also, you can do this in your view like the below code snippet:
my_dish = Dish.objects.get(pk=dish.id)
serializer = Dish_Serializer(my_dish)
desired_fields = {'id', 'name', 'description'}
all_fields = set(serializer.fields)
for field in all_fields:
if field not in desired_fields:
serializer.fields.pop(field)
my_new_object = serializer.data
I am copying a model object to another, but I want that it doesn’t copy the relations
For example, assume you have a model like this:
class Dish(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=500)
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1)
def __str__(self):
return self.name
Then I do:
my_dish = Dish.objects.get(pk=dish.id)
serializer = Dish_Serializer(my_dish)
my_new_object = serializer.data
I want my_new_object
to include only those attributes that are not relations, in this case, name and description.
How do I do that without accessing name and description directly?
I assume in your serializer you don’t want to explicitly define which field to serialize. Otherwise you could do the following:
class Dish_Serializer(serializers.ModelSerializer):
class Meta:
model = Dish
fields = ['id','name', 'description']
You probably can define these fields dynamically:
fields = [f.name for f in Dish._meta.concrete_fields]
or
fields = [f.name for f in Dish._meta.fields if not isinstance(f,ForeignKey)]
Ultimately, you want my_new_object in dictionary format and as per condition pk will give you only one object of dish.
So, you can do this instead :
my_new_object = Dish.objects.filter(pk=dish.id).values("name", "description")[0]
It will give you exact what you want, just declare the fields you need in values as an attribute fields.
You can remove a field from your serializer using .fields.pop(field_name)
method like the below example According that I took from Dynamically modifying fields
[drf-docs]:
class DynamicFieldsModelSerializer(serializers.ModelSerializer): """ A ModelSerializer that takes an additional `fields` argument that controls which fields should be displayed. """ def __init__(self, *args, **kwargs): # Don't pass the 'fields' arg up to the superclass fields = kwargs.pop('fields', None) # Instantiate the superclass normally super().__init__(*args, **kwargs) if fields is not None: # Drop any fields that are not specified in the `fields` >argument. allowed = set(fields) existing = set(self.fields) for field_name in existing - allowed: self.fields.pop(field_name)
Also, you can do this in your view like the below code snippet:
my_dish = Dish.objects.get(pk=dish.id)
serializer = Dish_Serializer(my_dish)
desired_fields = {'id', 'name', 'description'}
all_fields = set(serializer.fields)
for field in all_fields:
if field not in desired_fields:
serializer.fields.pop(field)
my_new_object = serializer.data