How to update primary key field using Django Rest Framework - django

I have set field called cus_id into primary key field which is not AutoField.
I need to change pk alon to keep other relevant data for same id should be in same index instead pk alone need to be change.
if i gave request via PATCH/PUT it creates new record instead of updating PK
so i have gone through django docs which says,
The primary key field is read-only. If you change the value of the primary key on an existing object and then save it, a new object will be created alongside the old one.
but i am using Django Rest Framework for my API generation..
I can achieve it through overriding DRF GET methods. but i want to do without overridden.
is there any django way to update PK using DRF without overriding get method?
My view:
class ModelViewSet(ModelCustomViewSet):
model = Model
queryset = Model.objects.all()
serializer_class = ModelSerializer
filter_fields = model._meta.get_all_field_names()
filter_backends = [DjangoFilterBackendExt, ]
Serializer:
class ModelSerializer(RequiredMixin):
class Meta:
model = Model
update_lookup_field = "cus_id"

I'd suggest you forget about using custom field for PK and go with regular auto id. Alongside you still can use you your cus_id, requiring it to be non-nullable and unique. That way you can update it via a regular form with absolutely no problems.
Otherwise, you can do update it as you do, clone old instance fields contents into new one and delete old one.

Related

How to replace foreign key by its associated value in django queryset

I'm fairly new to django and i would need your help!
I wrote an api/route view that query the database and return a JSON to my fetch function in my javascript.
Is there a way to query the database and got back a queryset with foreign key replaced by its associated value ?
I though i could use ModelName.objects.select_related( 'field ') but i didn't understand how it works.
If you have a solution or advices on better way to achieve the goal, Thanks in advance!
Context in pseudo-code:
// HTML //
Button onclick function get_list_of_data
// JS //
function get_list_of_data:
Fetch URL of route django
Convert response to JSON
iterating through JSON to fill HTLM div
// Django //
use ModelName.objects.filter( name = name ) to get list of data in Queryset
use serializers.serialize(json, ...") to get JSON from Queryset
return JsonResponse (json_query)
If I understood the problem well, when you serialize a model that has a ForeignKey field defined you get only the id value in JSON response but you would like to get the whole object (not only a number) returned.
The way to do that is to specifically write serializer for that ForeignKey model and then use it within the serializer od the model that you are trying to fetch.
You haven't provided any code, but here is some example that might help you:
class SecondModelSerializer(serializers.ModelSerializer):
class Meta:
model = SecondModel
fields = '__all__'
class FirstModelSerializer(serializers.ModelSerializer):
foreign_key_field = SecondModelSerializer()
class Meta:
model = FirstModel
fields = ('id', 'foreign_key_field', 'field1', 'field2')
Here in your FirstModelSerializer you specifically told Django to use SecondModelSerializer for your ForeignKey field (I named it foreign_key_field). This way Django will know how to serialize that field instead of returning only the id value.

Django - force pk_url_kwarg to query other model instances

Consider the following code:
views.py
class BHA_UpdateView(UpdateView):
model = BHA_overall
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
To my understanding, pk_url_kwarg = 'pk_alt' will query and return instances of model = BHA_overall.
Is there any way that I can force pk_url_kwarg to query
& return other model instances defined in models.py (like model = other_model), while having my get_object() method to return objects in model = BHA_overall? What CBV should I use (I think UpdateView is not a good choice in this case)?
++ I'm trying to make a page that allows users to manage information about the product they use. So, ultimately I will implement forms, and the user input needs to be saved in DB
++ I need pk_url_kwarg = 'pk_alt' to query other models and generate url. But I still need get_object() method to return objects in model = BHA_overall to generate form fields on the user side.
From my understanding you need a django form generated from BHA_overall, but the data should be saved to AnotherModel right?
I will propose 2 solutions to this problem, Choose what best fits you.
Multiple views:
Have multiple views for the task, What I mean is create a view which creates the form for the frontend using BHA_overall, you can create both Create and Update view this way and update view's initial could be overwritten so form will have expected value when editing. And now post the data to another view which handles the post data. This view can have your AnotherModel doing its thing.
Using Django Form:
If you dont like having multiple views, You can keep things simple by creating a form yourself. Create a DjangoForm with the same fields you want to show to the user and use it in to create your own views, Now you wont need BHA_overall and use your AnotherModel to save datal.

Django - copy and insert queryset clone using bulk_create

My goal is to create a clone of a queryset and then insert it into the database.
Following the suggestions of this post, I have the following code:
qs_new = copy.copy(qs)
MyModel.objects.bulk_create(qs_new)
However, with this code I run into duplicate primary key error. As for now, I only can come up with the following work-around:
qs_new = copy.copy(qs)
for x in qs_new:
x.id = None
MyModel.objects.bulk_create(qs_new)
Question: Can I implement this code snippet without going through loop ?
Can't think of a way without loop, but just a suggestion:
# add all fields here except 'id'
qs = qs.values('field1', 'field2', 'field3')
new_qs = [MyModel(**i) for i in qs]
MyModel.objects.bulk_create(new_qs)
Note that bulk_create behaves differently depending on the underlying database. With Postgres you get the new primary keys set:
Support for setting primary keys on objects created using
bulk_create() when using PostgreSQL was added.
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create
You should, however make sure that the objects you are creating either have no primary keys or only keys that are not taken yet. In the latter case you should run the code that sets the PKs as well as the bulk_create inside transaction.atomic().
Fetching the values explicitly as suggested by Shang Wang might be faster because only the given values are retrieved from the DB instead of fetching everything. If you have foreign key relations or m2m relations you might want to avoid simply throwing the complex instances into bulk_create but instead explicitly naming all attributes that are required when constructing a new MyModel instance.
Here an example:
class MyModel(Model):
name = TextField(...)
related = ForeignKeyField(...)
my_m2m = ManyToManyField(...)
In case of MyModel above, you would want to preserve the ForeignKey relations by specifying related_id and the PK of the related object in the constructor of MyModel, avoiding specifying related.
With m2m relations, you might end up skipping bulk_create altogether because you need each specific new PK, the corresponding original PK (from the instance that was copied) and the m2m relations of that original instance. Then you would have to create new m2m relations with the new PK and these mappings.
# add all fields here except 'id'
qs = qs.values('name', 'related_id')
MyModel.objects.bulk_create([MyModel(**i) for i in qs])
Note for completeness:
If you have overriden save() on your model (or if you are inheriting from 3rd party with custom save methods), it won't be executed and neither will any post_save handlers (yours or 3rd party).
I tried and you need a loop to set the id to None, then it works. so finally it may be like this:
qs_new = copy.copy(qs)
for q in qs_new:
q.id = None
# also, you can set other fields if you need
MyModel.objects.bulk_create(qs_new)
This works for me.

Creating manytomany objects on a fresh django db object

I am using Django-rest-framework and am trying to add tags to my models.
Every thing is ready on the database-side, but how do I do it on the django-rest side?
Simplified, my model looks like:
name = models.CharField()
tags = models.ManyToManyField(Tags)
I am presenting the tags as a comma-list in django-rest to make it easy for people using the API to add and change tags. However, how can I add tags to a object that doesn’t exists yet?
Using django-rest restore_object in my serializer, I am able to create the list of manytomany objects, but how do I add them to django-rest attrs so it will add them to my object?
In short, how can I add a list of items to .tags in django-rest restore_object function?
Or is this impossible and I need to do the tags handeling -after- the object is created and therefor hide the "tags" field when creating the object in django-rest and display it at the detailed serializer page instead?
I'm not sure if this is the best method, but this seems to work with overriding the save_object method in the serializer.
def save_object(self, obj, **kwargs):
super(MySerializer, self).save_object(obj, **kwargs)
tags = self.init_data.get('tags', None)
if tags:
obj.tags.clear()
tags = tags.split(',')
for t in tags:
tag_obj, created = Tag.objects.get_or_create(name=t, owner=self.context['request'].user)
obj.tags.add(tag_obj)
Is it a bad idea to grab this data from init_data? Seems a little dirty...

How to get the ID of a just created record in Django?

I'm using Django 1.3 for one of my projects and I need to get the ID of a record just saved in the database.
I have something like the code below to save a record in the database:
n = MyData.objects.create(record_title=title, record_content=content)
n.save()
The ID of the record just saved auto-increments. Is there a way to get that ID and use it somewhere else in my code?
Use n.id after the save.
See "Auto-incrementing primary keys".
It would be n.pk.
To quote "Model.pk":
Regardless of whether you define a
primary key field yourself, or let
Django supply one for you, each model
will have a property called pk. It
behaves like a normal attribute on the
model, but is actually an alias for
whichever attribute is the primary key
field for the model. You can read and
set this value, just as you would for
any other attribute, and it will
update the correct field in the model.
The ID will be automatically updated in your model, so immediately after your n.save() line you can read n.id and it will be populated.
Remove save() and get pk directly:
n = MyData.objects.create(record_title=title, record_content=content)
n.pk
If someone reading this question and after check the other answers still having problems accessing the id after the creation of the object.
Be sure you don't define id as an Integer in your model. If you decide to declare it anyways, use Autofield but you don't need to, It is for free with models.Model
#No
class TestModel(models.Model):
id = models.IntegerField(primary_key=True)
something...
#Ok
class TestModel(models.Model):
id = models.AutoField(primary_key=True)
something...
#Ok
class TestModel(models.Model):
something...
if you do define id as Integer, TestModel.objects.create( or with save() will return None.
I had a similar issue with accessing the id. In Django 3.0.5, this is how I accessed the id. Using your example and variable name, see below:
instance = n.save()
# return the id
instance[0].id
The variable 'instance' above is a list. Accessing id in the methods described above returns an AttributeError ("object has no attribute 'id'") in Django 3.
This answer applies when using modelformset_factory. This is true when creating a Form class from a Django model as described in the Django docs