I'm wondering whether it's possible to do something like the following
class ModelA(models.Model):
my_field = models.ForeignKey('UniqueKey', to_field='key' default=UniqueKey.create(key=KeyGen()))
# KeyGen() returns a new random key
In essence, I have a number of models that have a unique 32 digit key as one of their fields. I have created the UniqueKey model to manage all of those keys and make sure no duplicates ever arise. What I'm trying to do is create a new UniqueKey instance and save it to the database whenever I make a new ModelA instance.
Is this possible or am I just going about this the wrong way?
I think your best approach will be to use the post_save signal when an instance of your model is created. https://docs.djangoproject.com/en/1.9/ref/signals/#post-save
i.e.
class ModelA(models.Model):
my_field = models.ForeignKey('UniqueKey', to_field='key')
def set_default_uniquekey_for_modela(sender, instance, created, raw, using, update_fields):
if created is True:
new_unique_key = UniqueKey(key=KeyGen())
new_unique_key.save()
instance.my_field = new_unique_key
instance.save()
post_save.connect(set_default_uniquekey_for_modela, sender=ModelA)
So when an instance of ModelA is created, it will exectute set_default_uniquekey_for_modela(...) to create a new UniqueKey instance and assign that to the instance.
Related
I was thinking of creating an instance of a foreignkey field and referring it every time an instance of a model is created, but I didn't find any solution for this. Usually we need to create a model of foreignkey type and then refer to it from the model, but I want it to automatically create one foreignkey instance from the beginning of and always refer to it.
To provide an example let's say we've 2 model fields called User and WeeklySchedule. Everytime an instance of a User is created,another corresponding instance of a WeeklySchedule model is also created that will be referred to by the instance of the User.
We can do this inside save() method of the User model.
def save(self, *args, **kwargs):
schedule = create_and_or_get_new_weekly_schedule()
""" where create_and_or_get_new_weekly_schedule either creates a new
instance or gets that of the foreignkey model
"""
self.availability_schedule_tutor = schedule
super().save(*args, **kwargs)
We can also set the on_delete option of the foreignkey field to models.PROTECT or models.RESTRICT to make sure it never loses reference. Also make sure to set null=True or else an instance of a user can never be created.
Something like the following:
weekly_schedule = WeeklySchedule()
# Set your weekly_schedule fields here
weekly_schedule.save()
user = User()
# Set your user fields here
user.weekly_schedule = weekly_schedule
user.save()
What is the right way to lookup a table and use its last value as a value in a new model instance? Something like this:
class MyClass(models.Model):
id = models.AutoField(primary_key=True)
obj = MyClass.objects.latest('id')
my_field = models.IntegerField(default=(obj+1))
I need a db column, which keeps track of the primary_key, but is independent of it and can also be modified. Also I need to use its value as default when creating new instances of the Model.
you can use custom constructor as described in the docs:
https://docs.djangoproject.com/en/2.2/ref/models/instances/
you will need to define the obj field either as integer(to store the id of the previous record) or as a foreign key(if you want to reference the previous db record). In the second case you will need to pass the name of the model to the ForeignKeyField constructor as string('MyClass') and not directly(MyClass).
Supposing I already have a created instance of a Django's model. I want to get another instance that is a duplicate in the database. Is there a universal way to do it without finding out which unique index is responsible for this duplication:
class MyModel(models.Model):
...
instance = MyModel(...)
print(instance.id) # None
...
duplicate = get_duplicate(instance) # what should I type here instead of get_duplicate?
print(duplicate.id) # Some ID in DB
I want the function get_duplicate to not depend on the internal structure of the model. Also I don't want to modify the model.
For example, if I need to find out a duplicate exists I can do instance.save(). In case of IntegrityError there's a duplicate. But how to find out which one?
When you instantiate a model as such MyModel(...), you get an unsaved instance. To propagate it to the database, you have to call .save() on it, at which point instance.id will be set to something. You could also do MyModel.objects.create(...) as a shortcut.
Now, to answer the question, to duplicate a record you already have in the database; set its id to None, and save it again.
instance = MyModel.objects.get(id=1)
instance.id = None
instance.save()
print(instance.id) # 2
If I understand your question correctly, you want .save() to create two database rows instead of one? I don't understand why you'd want that, or how you'd make it useful, but you'd do it by overriding .save() on your model:
class MyModel(models.Model):
...
def save(self, *args, **kwargs):
instance = super().save(*args, **kwargs)
instance.id = None
instance.save()
return instance
As for finding a duplicate instance, this is much more difficult. You're going to have to decide what makes an instance a "duplicate". If it's a user model, for instance, maybe if only the email address is the same, it's a duplicate, but if it's a transaction instance, then EVERY field has to be the same.
As this is to inextricably linked to the model's type, you will want to put this on the model itself. I'll write a toy example below:
class MyModel(models.Model):
a = models.CharField(unique=True, ...)
b = models.CharField(unique=True, ...)
c = models.CharField(...)
def get_duplicates(self):
return type(self).filter(
a=self.a,
b=self.b,
)
In this example, a and b must match, but c doesn't have to.
You've already defined what makes a model a "duplicate" with your unique and unique together keys, so your .get_duplicates() function should be informed by those.
I have two models that look like this:
class ModelOne(models.Model):
foo = models.CharField(max_length=25)
def save(self,*args,**kwargs):
a = ModelTwo.objects.get(pk=arbitrary_pk)
a.somefield.add(self) # I am worried about this line here
super(ModelOne,self).save(*args,**kwargs)
class ModelTwo(models.Model):
somefield = models.ManyToManyField(ModelOne)
The line where I am adding self to a.somefield is the line I am worried about. How can I do this without error? Currently, I am getting:
ValueError: Cannot add "<ModelOne>": the value for field "modelone" is None
Thanks in advance
You can't do that because when you call .add() you have yet to save your model. That means that the model may not have been created (so it doesn't have an ID yet).
Basically you're telling Django to update the Foreign Key with something that doesn't exist yet (NULL), which will error out. You need to make sure the model has been created before you can set the foreign key.
try moving the a.somefield.add(self) to AFTER the super() call.
You cannot save many to may field before calling actual save method, you modify code like,
def save(self,*args,**kwargs):
super(ModelOne,self).save(*args,**kwargs) # Here your self has been saved
a = ModelTwo.objects.get(pk=arbitrary_pk)
a.somefield.add(self) # Now your self can be add as ManyToMany as it is already saved in db
I hope this help.
Add the instance to the many to many field after calling the save method.
class ModelOne(models.Model):
foo = models.CharField(max_length=25)
def save(self,*args,**kwargs):
super(ModelOne,self).save(*args,**kwargs)
a = ModelTwo.objects.get(pk=arbitrary_pk)
a.somefield.add(self) #add self to the object manytomany.
a.save() #save the object.
class ModelTwo(models.Model):
somefield = models.ManyToManyField(ModelOne)
You need to save the self object first. The many to many relation needs to have the related object saved in the database first, inorder to define the relationship. Then, define the relationship using a.somefield.add(self). Then, save the a object. Otherwise, the relation won't be committed in the database.
I ended up utilizing post_save to get this to work.
I have a situation where I need to notify some users when something in DB changes. My idea is to catch pre_save and post_save signal and make some kind of diff and mail that. Generally it works good, but I don't know how to get diff for m2m fields.
At the moment I have something like this:
def pre_save(sender, **kwargs):
pk = kwargs['instance'].pk
instance = copy.deepcopy(sender.objects.get(pk=pk))
tracking[sender] = instance
def post_save(sender, **kwargs):
instance = copy.deepcopy(kwargs['instance'])
print diff(instance, (tracking[sender])) # TODO: don't print, save diff somewhere
Diff function should work for every model (at the mommet I have four model classes). With deepcopy I can save old model, but I don't know how to save m2m fields because they are in separate table (yes, I know I can get this data, but at the momment of execution I don't know what fields are m2m and I wouldn't like to create different slot for every model). What I would like is generic solution, so I can just add models later without thinking about notification part.
My plan is to call get_data() and clear_data() functions after save() in view to clean diff that slots have generated.
Is this good way of doing this? Is there a better way? Is there django application that can do this job for me?
Excuse my English, it's not my native language.
First of all, you don't need to use deepcopy for this. Re-querying the sender from the database returns a "fresh" object.
def pre_save(sender, **kwargs):
pk = kwargs['instance'].pk
instance = sender.objects.get(pk=pk)
tracking[sender] = instance
You can get a list of all the many-to-many fields for a class, and check the values related to the current instance:
for field in sender._meta.local_many:
values = field.value_from_object(instance).objects.all()
# Now values is a list of related objects, which you can diff