Django: Does "primary_key=True" also mean "unique"? - django

Hello i am testing Django authentication and nesting user data. I created a simple MyProfil model for my users. I wanted to test making a custom id and set the primary_key=True as id = models.UUIDField.
models.py
class MyProfil(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
owner = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
aboutme = models.TextField(max_length=300, blank=True)
city = models.TextField(max_length=300, blank=True)
so far everything works in my favor but i have a question, that i could not answer myself even after reading the django doc.
Question
Does primary_key=True on my id Field also mean unique or do i have to declare it?

Yes. Since a primary key means a value that can uniquely identify an object. In the documentation on the primary_key parameter, we see:
Field.primary_key
If True, this field is the primary key for the model.
If you don’t specify primary_key=True for any field in your model,
Django will automatically add an AutoField to hold the primary key,
so you don’t need to set primary_key=True on any of your fields
unless you want to override the default primary-key behavior. For
more, see Automatic primary key fields.
primary_key=True implies null=False and unique=True. Only one primary key is allowed on an object.

Related

Setting default value of field in model to another model instance

Model
class SlackPermission(models.Model):
#fields
class GithubPermission(models.Model):
#fields
class Employee(models.Model):
#fields
slack_permission = models.OneToOneField(SlackPermission, on_delete=models.CASCADE, related_name='Slack',default=SlackPermission.objects.get(pk=1))
github_permission = models.OneToOneField(GithubPermission, on_delete=models.CASCADE, related_name='Github',default=GithubPermission.objects.get(pk=1))
Error:
ValueError: Cannot serialize: <GithubPermission: GithubPermission object (1)>
There are some values Django cannot serialize into migration files.
I am creating API just to create Employee. Where there is not option of giving slackpermissions and githubpermissions. How do I give default value in there?
The problem is that the default is calculated immediately, and for migrations, it can not really serialize that.
That bing said, it is not very useful to do this anyway. You can just pass the primary key as default value. This is specified in the documentation on the default=… parameter [Django-doc]:
For fields like ForeignKey that map to model instances, defaults should be the value of the field they reference (pk unless to_field is set) instead of model instances.
So we can write this as:
class Employee(models.Model):
full_name = models.CharField(max_length=100)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
slack_permission = models.OneToOneField(
SlackPermission,
on_delete=models.CASCADE,
related_name='Slack',
default=1
)
github_permission = models.OneToOneField(
GithubPermission,
on_delete=models.CASCADE,
related_name='Github',
default=1
)
Note that you should ensure that there exists an object with that primary key. Therefore it might not be ideal to do that.
The issue here is that you are attempting to set a field value to an object instance. So your default value should be just 1 if you are certain of the pk.
Also, I am not sure the advantage of creating two separate models for these permission values. Seems like they can just be fields in your employee model. Seems like these permissions share identical fields as well which will allow you to flatten them a bit.

How to define a serializer for model with a foreign key (not pointing to a pk field but a unique field)?

I am using django rest-framework
There are two modes as below
class Service(ResourceModelBase):
_id = models.UUIDField(unique=True, null=False, default=uuid.uuid1)
access_key = models.CharField(max_length=200, unique=True, null=False)
cluster = models.OneToOneField(Cluster, to_field='_id')
class Cluster(ResourceModelBase):
_id = models.UUIDField(unique=True, null=False, default=uuid.uuid1)
name = models.CharField(max_length=200, unique=True, null=False)
The service have a foreign key point to the cluster unique key _id field please be noticed that _id is not the primary key, it is a unique key. This is
important to my later question.
Then I define a serializer for the service model, so I can create and get the service data.
class ServiceSerializer(serializers.ModelSerializer):
cluster_id = serializers.SlugRelatedField(queryset=models.Cluster.objects.all(), slug_field='_id')
class Meta:
model = models.RGW
fields = ('_id', 'access_key', 'cluster_id')
Please be notice that I define the cluster_id as a SlugRelatedField not the PrimaryKeyRelatedField.
The reason to do so is I want to pass the cluster_id a parameter to create service.
If I define cluster_id as primary key related field, the framework tend to think the cluster_id foreign key is pointing to a primary key column. But in my case it is not. It is pointing to a unique key field .
So I use SlugRelatedField(I `m not sure if this is the right way to do). But another problem shows up.
When I send data to serializer. The data I send in is as below:
{'cluster_id': 'ef3f70cac9e111e89fd1f000ac192ced', 'access_key': 'aaaa'}
But data after being validated by serailzier is
serializer.validated_data
OrderedDict([('access_key', 'aaaa'), ('cluster_id', <Cluster: Cluster object (2)>)])
For some reason the cluster_id is converted to cluster object. So If I run
serializer.save(serializer.validated_data)
This will cause error.
Of course I could re-write the save method. But I just want to know what is the best way to do this. I feel I am using the framework in a wrong way

Why do I get a ForeignKey constraint violation

As can be seen below, I have 2 models connected via an intermediary model to form a ManyToMany relationship. The problem is that, when I delete a Tender object in get this error.
update or delete on table "tender_details_tender" violates foreign key constraint "user_account_company_tender_id_984ea78c_fk_tender_de" on table "user_account_companyprofile_assignedTenders"
DETAIL: Key (id)=(1) is still referenced from table "user_account_companyprofile_assignedTenders".
I thought by adding on_delete=models.CASCADE in the ForeighKeys (i.e. in the intermediary model) would solve this problem, but apparently not.
class CompanyProfile(models.Model):
assignedTenders = models.ManyToManyField(Tender, through='UsersTenders', related_name='UserCompanies')
# connects users to the tenders they match.
class UsersTenders(models.Model):
user = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE, related_name='userTenderLink')
tender = models.ForeignKey(Tender, on_delete=models.CASCADE, related_name='userTenderLink')
sent = models.BooleanField(default=False, blank=False)
class Meta:
unique_together = ("user", "tender")
class Tender(models.Model):
tenderCategory = models.ManyToManyField(Category, blank=False) #this field holds the tender category, e.g. construction, engineering, human resources etc.
tenderProvince = models.ManyToManyField(Province, blank=False) #this is the province the tender was a
For what its worth, I know what is causing this problem, what I don't know is how to fix it. The problem is that initially I had the ManyToManyField under the CompanyProfile model without the "through" argument, so as you might imagine Django created it's own intermediary table which is "user_account_companyprofile_assignedTenders" as shown in the error. I later decided to create my own intermediary model (i.e. UsersTenders) because I wanted an extra field there, so I had to add the "through" argument in my ManyToManyField (i.e. 'assignedTenders'). That worked fine but the old intermediary model "user_account_companyprofile_assignedTenders" did not get deleted automatically, I assume its because a few relationship had be created before the change. How can I delete "user_account_companyprofile_assignedTenders" without destabilizing my project.
Did you add on_delete after database migration? If so have you made a migration after adding on_delete?
You could try to set null=True to all fields and then try ti figure out which foreign key is causing problems.
bdw. when you set blank=True that only means that your form fields will not insist on this fields to be filled for submitting.

Integrity_Error (key is still referenced) thrown even with on_delete=do_nothing

I have a model:
class Project(Model):
name = models.TextField(...)
... bunch of fields ...
I also have some models that track history in my application. These models have ForeignKey references to the Project model:
class Historical_Milestone(Model):
project = models.ForeignKey('Project', db_index=True, related_name='+',
on_delete=models.DO_NOTHING, blank=True, null=True)
When I delete an item in my Project table, I get the following IntegrityError:
update or delete on table "project" violates foreign key constraint {...} on table "historical_milestone"
DETAIL: Key (id)=(123) is still referenced from table "historical_milestone".
The column in the historical table has related_name='+' set (indicating that I don't want the reverse lookup), and I have the on_delete=models.DO_NOTHING parameter set. According to the documentation on the DO_NOTHING option:
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.
Shouldn't the related_name flag indicating I don't care about the reverse relationship take care of this? What manual step do I need to take to modify the Historical_Milestone table to prevent this error from happening?
The related_name is for convenience, it doesn't affect the foreign key constraints that Django creates. Setting related_name='+' simply means you don't want to be able to access product.historical_milestone_set (or similar) in the ORM.
If you want to keep the id after the project is deleted, and not enforce a foreign key constraint, it might be better to use an integer field.
class Historical_Milestone(Model):
project_id = models.PositiveIntegerField(db_index=True, blank=True, null=True)

Django model foreign key removal

I have following model:
class Client(models.Model):
user = models.OneToOneField(DjangoUser, unique=True)
address = models.ForeignKey(Address,blank=True)
class Address(models.Model):
(...)
Then I do:
client=Client()
client.address=address #any Address instance
client.save()
And now: how can I remove foreign association key from client?
client.address=None
seem not to work.
To be able to null out a foreign key, it's not enough to set in blank. You must also specify that null=True is also set on the field. See The Difference Between Blank and Null.
Your current models setup does not allow null=True, thus you cannot set it to None.
address = models.ForeignKey(Address,blank=True, null=True)
the key is null=True as well as blank=True
also, make sure to syncdb etc