How to set custom text for django on_delete function? - python

I have a model named 'Clients' and another model named 'Information'.
class Information(models.Model):
client_name = models.ForeignKey(Clients, on_delete=models.SET(get_deleted_client_intance))
I want to set a custom text when a name is deleted from 'Clients' model.
The below function creates a new name 'deleted' as a new entry and is saved in 'Clients' model. I don't want that. I want if I delete a name it says 'deleted' or 'removed' on it's place.
def get_deleted_client_intance():
return Clients.objects.get_or_create(name='deleted')[0]
How can I do this?

I think you have a little misunderstanding about on_delete keyword argument. According to the docs:
When an object referenced by a ForeignKey is deleted, Django will emulate the behavior of the SQL constraint specified by the on_delete argument. For example, if you have a nullable ForeignKey and you want it to be set null when the referenced object is deleted:
user = models.ForeignKey(
User,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
I.e. it handles some logic when a referenced object is deleted - sets the field as null (SET_NULL), restricts from removing if the referenced object has children (PROTECT), deletes the child objects as well (CASCADE), or sets your preferred value (SET), etc.
In your case, if you want to change the state of the Clients object you don't need to do it in the callable passed to models.SET, you'd better do it on the object level, e.g. override the delete method:
class Clients(models.Model):
...
def delete(self, using, *args, **kwargs):
self.name = 'deleted'
self.save(using=using)

Related

django model inheritance: How does model creation work with custom manager?

Recently I've faced an issue with Django, model inheritance and how the creation of model instances works.
Suppose I have the following (basic) setup:
class InviteBaseManager(models.Manager):
def create(self):
new_code = # create some kind of unique code, not really relevant here.
return super().create(code=new_code)
class InviteBase(models.Model):
code = models.CharField(max_length=10, blank=False, null=False, unique=True)
creationDate = models.DateTimeField(default=timezone.now())
objects = InviteBaseManager()
class PartyInviteManager(models.Manager):
def create(self, name):
# method 1
newInvite = InviteBase.objects.create()
print(newInvite.code) # is definitly set, lets assume "ABCD"
# as expexted, the "InviteBase" table has one row with code "ABCD" and
# default creationDate
newPartyInvite = super().create(partyName=name, invite=newInvite)
print(newPartyInvite.invite.code) # is EMPTY, prints nothing
# In fact, when looking at the db, there is still only *one* row in the table "InviteBase",
# with an *empty* code field and a default creationDate field.
return newPartyInvite
#method 2
newPartyInvite = super().create(partyName=name)
# creates the InviteBase instance implicitly, again, newPartyInvite.invite.code is empty.
# fill newPartyInvite.invity.code manually.
return newPartyInvite
class PartyInvite(InviteBase):
#Isn't blank=False and null=False unnecessary? A child should probably not exist with no parent?
invite = models.OneToOneField(InviteBase, parent_link=True, on_delete=models.CASCADE, null=False, blank=False)
partyName = models.CharField(...)
objects = PartyInviteManager()
So the question is: How can I pass an already existing instance of the base class inside the create method of my PartyInviteManager? Even when using method 1, the existing instance that I pass along seems to be overwritten. A new one is created with the default value. Interestingly enough, this violates the constraints that code cannot be blank or NULL.
This behaviour seems a bit odd to me? Can someone point out what I am missing here?
To clarify: I know that I should usually use **kwargs in the create methods and that inheritance might not be the ideal use case here, but I'm just very curious about this behaviour.
I know that this kind of model inheritance wont even create a pk for the child model (because holding a OneToOneField to the parent class effectively acts as a pk anyway), but why would it be impossible to pass a manually created instance as parent? Am I not allowed to use inheritance for my use-case?
I think I've found the correct way of doing this:
class InviteBaseManager(models.Manager):
def create(self, **kwargs):
kwargs['code'] = #createCode
return super().create(**kwargs)
class InviteBase(models.Model):
code = models.CharField(max_length=10, blank=False, null=False, unique=True)
creationDate = models.DateTimeField(default=timezone.now())
objects = InviteBaseManager()
class PartyInviteManager(InviteBaseManager):
def create(self, name):
return = super().create(partyName=name)
class PartyInvite(InviteBase):
invite = models.OneToOneField(InviteBase, parent_link=True, on_delete=models.CASCADE, null=False, blank=False)
partyName = models.CharField(...)
objects = PartyInviteManager()
The PartyInviteManager now inherits from the InviteManager as well. When calling super.create() from the child manager, the base manager gets called, appends the code field and everything works as expected.
I also found out, that if the PartyInviteManager does not inherit from the InviteBaseManager, the create method of InviteBaseManager does not get called when creating a new PartyInvite. This seems very odd to me.
Of course, the easier way would have been to create the code as default value via a function, like that:
def createCode():
return "ABCD" # add fancy code creation magic here.
class InviteBase:
code = models.CharField(max_length=128, blank=False, null=False, default=createCode)
But if, depending on the child class, the code needs additional information (such as invited members or whatever), this approach would not work anymore.
On a side note, interestingly enough the following code snippet:
invite = PartyInviteManager.objects.create(partyName='Birthday')
print(invite.invite.code)
print(invite.code)
produces the following output:
ABCD
ABCD
In the create method of the PartyInviteManager, one can directly use code=XXXX to pass along a string for the InviteBase model, invite_code on the other hand does not work.

Django model's table doesnt contain given field

I have the following model in Django:
class Priority(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
priority = models.PositiveSmallIntegerField
However, when I run makemigrations and migrate, the field priority ( models.PositiveSmallIntegerField) doesn't appear in mysql table and I cannot create object of this model. Why?
Because you did not construct a field, you only passed a reference to that class, you need to add brackets (()) to make the call:
class Priority(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
priority = models.PositiveSmallIntegerField()
# call the constructor ^^
If you do not call the constructor, you have set priority as a reference to the PositiveSmallIntegerField class, not as a PositiveSmallIntegerField object.

TypeError in models

I'm getting this error:
user = models.OneToOneField(User)
TypeError: init() missing 1 required positional argument: 'on_delete'
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfileInfo(models.Model):
# creating relationship
user = models.OneToOneField(User)
# additional attributes
portfolio = models.URLField(blank=True)
picture = models.ImageField(upload_to='profile_pics', blank=True)
def __str__(self):
return self.user.username
As the error indicates, you need to specify what should happen, given the object to which you refer is removed, by the on_delete= parameter [Django-doc]. For example:
class UserProfileInfo(models.Model):
# creating relationship
user = models.OneToOneField(User, on_delete=models.CASCADE)
# additional attributes
portfolio = models.URLField(blank=True)
picture = models.ImageField(upload_to='profile_pics', blank=True)
def __str__(self):
return self.user.username
The options here are:
CASCADE
Cascade deletes. Django emulates the behavior of the SQL constraint
ON DELETE CASCADE and also deletes the object containing the
ForeignKey.
Model.delete() isn't called on related models, but the pre_delete
and post_delete signals are sent for all deleted objects.
PROTECT
Prevent deletion of the referenced object by raising ProtectedError,
a subclass of django.db.IntegrityError.
SET_NULL
Set the ForeignKey null; this is only possible if null is True.
SET_DEFAULT
Set the ForeignKey to its default value; a default for the
ForeignKey must be set.
SET()
Set the ForeignKey to the value passed to SET(), or if a callable
is passed in, the result of calling it. In most cases, passing a
callable will be necessary to avoid executing queries at the time your
models.py is imported (...)
DO_NOTHING
Take no action. If your database backend enforces referential
integrity, this will cause an IntegrityError unless you manually add
an SQL ON DELETE constraint to the database field.
A similar question is answered here Getting TypeError: __init__() missing 1 required positional argument: 'on_delete' when trying to add parent table after child table with entries
Basically following should fix url problem
From Django 2.0 on_delete is required:
user = models.OneToOneField(User, on_delete=models.CASCADE)
put 'on_delete = models.CASCADE' in the constructor
what it does: when you will delete the user object as you have referenced it in the user field of your model. it will also delete the model object of UserProfileInfo for taht particular user.

Updating a field to the primary keys value after a model is created in Django 2.1

I have a Django model named Project that has recursive foreign keys to itself.
class Project(models.Model):
project_root_parent = models.ForeignKey('self',on_delete=models.CASCADE, related_name='root_parent',null=True)
project_parent = models.ForeignKey('self', on_delete=models.CASCADE, related_name = 'parent',null=True)
This is the desired functionality I want:
If the project does not have a parent project the field is set to the
created objects primary key.
If the project doesn't have a root parent project the field is set to
the created objects primary key.
Ideally i want the default value of the field to be set to the primary key.
If this is not possible then I need to be able to update the value after the primary key is generated and the model is instantiated in Django.
I have looked at overriding the save method or using the post_save signal but I am unsure that either of these methods are correct.
class Project(models.Model):
project_root_parent = models.ForeignKey('self',on_delete=models.CASCADE, related_name='root_parent',null=True, blank=True)
project_parent = models.ForeignKey('self', on_delete=models.CASCADE, related_name = 'parent',null=True, blank=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.project_root_parent is None:
self.project_root_parent = self
if self.project_parent is None:
self.project_parent = self
super().save(*args, **kwargs)
The trick is to make sure blank=True when creating the CharFields because its different than setting them to nullable.
Call the super constructor to save the fields as blank.
Once the project instance exists then change the fields to itself.
Then call the super constructor again to detect changes and save the model.

Django save and return object at once (with a custom pk)

I am creating the object of a model using custom create method create_actor like this:
class ActorsManager(models.QuerySet):
def create_actor(self, email,
actortype,
locationid,
primaryphone, actoruniversalid):
actor = self.model(email=email,
actortype=actortype,
locationid = locationid,
primaryphone=primaryphone, actoruniversalid= actoruniversalid)
actor.save(using='gpr')
return actor
actor_entry = Actors.objects.using('gpr').create_actor(email='', actortype=1, locationid = location_entry,primaryphone='', actoruniversalid= new_bluenumber)
I am not getting the recently created object in actor_entry variable,
may be i am doing something wrong, please help.
I'm using a before insert SQL trigger to generate uuid as pk(CharField here) in the database, so the object does have a pk (generated by the database) before its saved.
Actors Model
class Actors(models.Model):
actorid = models.CharField(db_column='ActorID', primary_key=True, max_length=255) # Field name made lowercase.
actoruniversalid = models.CharField(db_column='ActorBluenumber', unique=True, blank=True, null=True, max_length=254)
......
......
objects = ActorsManager.as_manager()
class Meta:
managed = False
db_table = 'actors'
Unless you created your actors table by hand, the ActorID will not automatically be set. Depending on your database backend, None may be treated the same as the empty string, and as such could be a valid primary key. I believe Django takes special measures to update the row if one exists with the same primary key unless you specify forceinsert=True when invoking save()
As #knbk pointed out that only an AutoField can set the pk to an object and so the pythonic logic to retrieve the pk is not executed when its not an AutoField and so django doesn't know about it.
After searching the django documentation I found an alternative to AutoField for UUIDs,an UUIDField which is best suited for my Use Case.
actorid = models.UUIDField(db_column='ActorID', primary_key=True, editable=False, default=uuid.uuid4)
So now the django does know of the pk and thus the object is retrieved after being saved.
Thank you all for the help.

Categories