primary key for users - django

primary key is created automatically for every row in table right, but for users isnt it better to use their username as primary key? or defaults better? or for posts like a music post or a picture shouldn't we use its name as primary key?
if yes how should i change urls ? i mean this is my urlspath('genre_list/<int:pk>/',views.GenreDetailView.as_view(),name='genre_detail'),and path is int shoul i just remove int and let it be like <pk> or text primary key has another way?

If I understood your question correctly, you would like to change your url from primary key (automatically assigned) to a custom slug (name/post-name/ etc)?
You should always keep primary key as the id, but if you would like to change it to something more unique than an integer, simply add this field to your model to override the basic int primary key:
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
To customize your url and not use the id/pk, simply add this to your model:
models.py
from django.db import models
from django.urls import reverse
import uuid, string
from django.utils.text import slugify
class Post(models.Model):
post_name = models.CharField(max_length=200, null=True, blank=True)
post_topic = models.CharField(max_length=200, null=True, blank=True)
slug = models.SlugField(null=True, unique=True, max_length=300)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
def get_absolute_url(self):
return reverse("post-page", kwargs={"slug": self.slug})
def save(self, *args, **kwargs):
self.slug = slugify(f"{self.post_name}-{self.post_topic}")
return super().save(*args, **kwargs)
urls.py
path('post-page/<slug>/', views.post-page, name='post-page'),
This will return a url of:
www.example.com/post-name-post-topic

Related

django model override save m2m field

I want to update m2m field on save() method
I have the following model:
class Tag(models.Model):
label = models.CharField(max_length=50)
parents_direct = models.ManyToManyField("Tag", related_name="children", blank=True)
created = models.DateTimeField(auto_now_add=True)
description = models.TextField(null=True, blank=True)
administrators = models.ManyToManyField(
to=KittyUser, related_name="administrated_tags", blank=True)
moderators = models.ManyToManyField(
to=KittyUser, related_name="moderated_tags", blank=True)
allowed_users = models.ManyToManyField(
to=KittyUser, related_name="allowed_tags", blank=True)
visible = models.BooleanField(default=True, verbose_name="visible to anyone")
POSTABILITY_CHOICES = (
('0', 'any allowed user can post'),
('1', 'only moderators\\admins can post')
)
postability_type = models.CharField(default='0',
max_length=1,
choices=POSTABILITY_CHOICES)
parents_tree = models.ManyToManyField("Tag", related_name="parents_tree_in_for", blank=True)
related_tags = models.ManyToManyField("Tag", related_name="related_tags_in_for", blank=True)
def save(self, *args, **kwargs):
self.label="overriden label"
super(Tag, self).save(*args, **kwargs)
self.parents_tree.add(*self.parents_direct.all())
breakpoint()
super(Tag, self).save(*args, **kwargs)
Through django-admin the tags get created, the label substitution works, though parents_tree don't get updated.
If I create it from the shell, it is swearing at the second super().save:
django.db.utils.IntegrityError: duplicate key value violates unique constraint "posts_tag_pkey"
If you take away the first super.save(), you get the following:
"<Tag: overriden label>" needs to have a value for field "id" before this many-to-many relationship can be used.
in both shell and admin.
The question is, how to update my m2m field on save?
Another question is why does it work differently in admin panel and shell?
As a temporary solution I managed to listen to the signal of updating parents_direct field, but what if I wanted to depend on non-m2m fields?
from django.db.models.signals import m2m_changed
def tag_set_parents_tree(sender, **kwargs):
if kwargs['action'] == 'post_add' or 'post_remove':
parents_direct = kwargs['instance'].parents_direct.all()
if parents_direct:
kwargs['instance'].parents_tree.set(parents_direct)
for tag in parents_direct:
kwargs['instance'].parents_tree.add(*tag.parents_tree.all())
else:
kwargs['instance'].parents_tree.clear()
m2m_changed.connect(tag_set_parents_tree, sender=Tag.parents_direct.through)

Django - Creating a row in onther table before creating user

I have user, userprofile and company tables.
I want to create a record in company table and then assign the id of the newly created company in the userprofile foreign key before creating the user. I think it can be done using pre_save signal but I am unable to figure how. Please help.
Here's some details as AMG asked:
I have django's builtin user model
a userprofile model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='profile_pics/default.jpeg', upload_to='profile_pics')
bio = models.TextField(blank=True, verbose_name='Biography')
company_name = models.ForeignKey(Company, on_delete=models.CASCADE)
a company model
class Company(models.Model):
name = models.TextField(blank=True, verbose_name="Company Name")
nof = models.IntegerField(verbose_name="No. of Employees")
All three are related user and userprofile has one to one relationship, and userprofile and company has many to one relationship.
I want Company record to be created first and than that new record's reference needs to be put into userprofile.
I think I have made it clear now.
Exactly, you can use pre_save for this. You can just put this in models.py below your defined models.
from django.db.models.signals import pre_save
#receiver(pre_save, sender=Profile) # register your model to the signal pre_save
def my_callback(sender, instance, *args, **kwargs):
if not instance.company_name: # check if instance has a value for company_name
company = Company.objects.create(
name='Test',
nof=1
)
instance.company_name = company
OR
Create a default value through a function.
class Company(models.Model):
name = models.TextField(blank=True, null=True, verbose_name="Company Name") # set blank=true and null=true so you can save an empty instance
nof = models.IntegerField(blank=True, null=True, verbose_name="No. of Employees")
def profile_company_default():
return Company.objects.create()
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='profile_pics/default.jpeg', upload_to='profile_pics')
bio = models.TextField(blank=True, verbose_name='Biography')
company_name = models.ForeignKey(Company, on_delete=models.CASCADE, default=profile_company_default) # add function as default value
EDIT
To edit company after save.
profile = Profile.objects.create(key="value")
profile.company.name = "Company Name"
profile.company.nof = 5
profile.save()

Add a prefix to primary key in django

I am trying to add a Prefix(YNT) to the primary key in django model
Models.py
class testmodel(models.Model):
product_id = models.IntegerField(max_length=500, primary_key=True)
product_short_code = models.CharField(max_length=500, default=0, null=True)
How Can I do That ? also instead of starting the primary key from 1 can I start it from 1000 ?
I want the table to look like this
product_id product_short_code
YNT1000 PP001
YNT1001 PL023
I think, It is not a good idea using string as a primary key. You can use helper method like on the following.
class testmodel(models.Model):
product_short_code = models.CharField(max_length=500, default=0, null=True)
def product_id(self):
return f'YNT{self.id}'
Please change IntegerField to CharField in testmodel class.
class testmodel(models.Model):
product_id = models.CharField(max_length=500, primary_key=True)
product_short_code = models.CharField(max_length=500, default=0, null=True)
If you want that your primary key start any given specific number. Please follow above link.
How can my Model primary key start with a specific number?
There are some approaches that you should follow to achieve this:
1. If Prefix is common for all the entries then you can create a field in your model and add the prefix while getting the value.
class testmodel(models.Model):
product_code = models.IntegerField(db_index=True)
product_short_code = models.CharField(max_length=500, default=0, null=True)
#property
def product_id(self):
return f"YNT{self.product_code}"
2. If Prefix is not common for all the entries then you can create a field in your model to store the prefix and add the prefix while getting the value.
class testmodel(models.Model):
product_code = models.IntegerField(db_index=True)
product_prefix = models.CharField(max_length=20, default='YNT')
product_short_code = models.CharField(max_length=500, default=0, null=True)
#property
def product_id(self):
return f"{self.product_prefix}{self.product_code}"
3. If you want to create CharField as primary_key then you
def create_id():
// code for creating unique ids. assuming the id will be stored in _id variable.
return f"YNT{_id}"
class testmodel(models.Model):
product_id = models.CharField(primary_key=True, max_length=20, default=create_id)
product_short_code = models.CharField(max_length=500, default=0, null=True)
With the 3rd approach you have to make sure the primary key is always unique.
I think you should see the primary key as a internal Django mechanism to reference and link objects.
Then if you want another, human readable id you can always create a char field product_id that would be as simple as 'YNT' + str(1000+id) which you can save using the post_save signal. You can also add an index to said field to make querying faster if needed.
Example:
#receiver(post_save, sender=Product)
def set_product_id(instance, created, **_kwargs):
if created:
instance.product_id = 'YNT' + str(1000 + instance.id)
But still, your relations should be on the default pk and not on the id.

How to obtain foreign key field name in post method?

I have an API which I pass some sample data. I am overwriting the post method by copying the Queryset so I can manipulate the output data. I would like to access the value of a foreign key relationship but I can only access the foreign key itself.
Like so:
def post(self, request, *args, **kwargs):
data_request = request.data
data_request = data_request.copy()
machine_na = data_request.__getitem__('project')
##this gives me the id of the foreign key
data_request.__setitem__('project', str('my value'))
return Response(data_request, *args, **kwargs)
Now that works. But I can't access the name of the foreign key.
My models as Foreign Keys:
class Machine(models.Model):
machine_name = models.CharField(max_length=120, primary_key=True)
def __str__(self):
return self.machine_name
class Project(models.Model):
project_name = models.CharField(max_length=120, primary_key=True)
def __str__(self):
return self.project_name
My model that refers to the others:
class Simulation(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
machine = models.ForeignKey(Machine, on_delete=models.CASCADE, default=1)
project
I hope I am clear enough. I would like to access the specific name of, for example, foreign key 2.
calling 'project.project_name' doesn't work and everything else I tried also not.
Any help is highly appreciated. Thanks in advance
Get the project object using the key and then get the name from the object.
project=Project.objects.get(id=int(self.request.POST['project']))
machine_na = project.project_name

Django Customize many to many query

I have extended Django's default User class by adding a ManyToManyField to another table called Algorithm. The new User class is:
class User(AbstractUser):
name = CharField(_("Name of User"), blank=True, max_length=255)
algorithms = ManyToManyField(Algorithm, blank=True, default=None, related_name="users")
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
I want to specify if the user is_superuser or is_staff then User.algorithms.all() will get all algorithms. Otherwise, get only the records in the pivot table User_user_models. How can this be achieved? I tried to add a property method that check if superuser/staff then return all otherwise return super but it didn't work.
P.S: during creating/editing the user, if user is set to superuser or staff, then there is no need to choose algorithms.
I would just overwrite the save() method:
class User(AbstractUser):
name = CharField(_("Name of User"), blank=True, max_length=255)
algorithms = ManyToManyField(Algorithm, blank=True, default=None, related_name="users")
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
def save(self, **kwargs):
if self.is_superuser or self.is_staff:
self.algorithms = Algorithm.objects.all()
else:
# Get your users from your pivot table
super().save()