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.
Related
I've got two models that I'd like to perform a reverse search on. I'm wondering how to do this given the fact that one model has to fields with foreign keys to the same model.
class Review(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, default=None)
class Cart(models.Model):
cost = models.DecimalField(max_digits=50, decimal_places=2, null=True, blank=True)
class Job(models.Model):
cart = models.ForeignKey(Cart, related_name="cart_one", on_delete=models.CASCADE, null=True, blank=True)
unscheduled_job = models.ForeignKey(Cart, related_name="cart_two", on_delete=models.CASCADE, null=True, blank=True)
employee = models.ForeignKey(Employee, on_delete=models.CASCADE, null=True, blank=True)
My query is as follows:
reviews = Review.objects.filter(cart__job__employee=employee)
This query is failing due to the fact that the Job model has two foreign keys that point to the cart model. How would I fix this?
Thanks!
If you specify a related_query_name=… parameter [Django-doc] or a **related_name=… parameter [Django-doc], then that is the name to access the model in reverse, so you can query with:
Review.objects.filter(cart__cart_one__employee=employee)
or if you want to query in reverse with the unscheduled_job, then it is:
Review.objects.filter(cart__cart_two__employee=employee)
You can also combine the two, so bo5th cart anfd unscheduled_job by making use of a Q object:
from django.db.models import Q
Review.objects.filter(Q(cart__cart_one__employee=employee) | Q(cart__cart_two__employee))
You might however want to change the related_name=…s, since this should be the name to access the Job object from the perspective of a Cart model.
I have a business model as follows:
class Business(models.Model):
class Meta:
verbose_name_plural = "Businesses"
name = models.CharField(max_length=60, null=False, verbose_name="Title")
about = models.TextField(max_length=5000, null=True, verbose_name="Description", blank=True)
upvote = models.ManyToManyField(Account, verbose_name="Upvote Count", blank=True)
The Account model is as follows:
class Account(models.Model):
CHOICES = [('Male', 'Male'),Female', 'Female'),]
class Meta:
verbose_name_plural = "Accounts"
name = models.CharField(max_length=60, null=False, verbose_name="Title")
gender= models.CharField(max_length=6, null=True, verbose_name="Gender", choices=CHOICES)
I am trying to get a QuerySet that will be sorted by gender of the Account.
How do I achieve this?
So far I have achieved sorting by the upvote count.
Business.objects.all().order_by("upvote")
You can use the Sum function [Django docs] with a Conditional Expression [Django docs] to annotate a value according to which you would order:
from django.db.models import Case, Sum, Value, When
Business.objects.annotate(
order_value=Sum(
Case(
When(upvote__gender='Male', then=Value(1)),
When(upvote__gender='Female', then=Value(-1)),
default=Value(0)
)
)
).order_by('order_value')
The above query would give you Business objects with more upvotes by females first and males later, you can reverse the order by writing .order_by('-order_value') instead.
You can access fiels of related models by double underscore. See documentation here.
Try:
Business.objects.all().order_by("upvote__gender")
The title is not entirely accurate, but I do not know how to explain, so I took a screenshot of where my problem is, and I will try to explain what I am trying to achieve.
I have a vehicle model, and when I create a new vehicle object, the name of vehicles just says Vehicle object (1) How can I change my model, or serializer (or something else) so that will show some unique value of the object.
Here is my model:
class Vehicle(models.Model):
make = models.CharField(max_length=100, blank=True)
model = models.CharField(max_length=300, blank=True)
license_plate = models.CharField(max_length=7, blank=True)
vehicle_type = models.CharField(max_length=20, blank=True)
seats = models.IntegerField(default=4,
validators=[
MaxValueValidator(70),
MinValueValidator(4)],
)
year = models.IntegerField(_('year'), default=datetime.today().year - 10, validators=[
MinValueValidator(1975), max_value_current_year])
inspected = models.BooleanField(default=True)
# fuel_type
# fuel_price
created_at = models.DateTimeField(auto_now_add=True)
You can implement a __str__ method that returns a string, for example:
class Vehicle(models.Model):
# …
def __str__(self):
return f'{self.make} ({self.year})'
You can thus return any string, and that will be presented by default in the Django admin and the drop downs.
These are my two models, when I try to open City page on Django I get an error: "column city.country_id_id does not exist". I don't know why python adds extra _id there.
class Country(models.Model):
country_id = models.CharField(primary_key=True,max_length=3)
country_name = models.CharField(max_length=30, blank=True, null=True)
class Meta:
managed = False
db_table = 'country'
class City(models.Model):
city_id=models.CharField(primary_key=True,max_length=3)
city_name=models.CharField(max_length=30, blank=True, null=True)
country_id = models.ForeignKey(Country, on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'city'
Because if you construct a foreign key, Django will construct a "twin field" that stores the primary key of the object. The foreign key itself is thus more a "proxy" field that fetches the object.
Therefore you normally do not add an _id suffix to the ForeignKey:
class City(models.Model):
city_id = models.CharField(primary_key=True,max_length=3)
city_name = models.CharField(max_length=30, blank=True, null=True)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'city'
It however might be better for unmanaged tables, to specify a db_column=… parameter [Djang-doc] in the ForeignKey:
class City(models.Model):
city_id = models.CharField(primary_key=True,max_length=3)
city_name = models.CharField(max_length=30, blank=True, null=True)
country = models.ForeignKey(Country, db_column='country_id', on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'city'
With this parameter you make it explicit how the column is named at the database side.
this is due to Django's behind the scenes magic.
The fields documentation is very clear about that and I highly recommend you read the Foreign Key section in the link below:
https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ForeignKey
Basically, when you want to access the Country reference in the if a City instance, you would do it like this:
city.country_id
I also recommend another naming convention for your Foreign Key fields. Instead of <modelname>_id = models.ForeignKey... just call it <modelname> = models.ForeignKey...
Hope this helps, happy coding
I'm working in Django 1.7 and Postgres, and using the ORM to create some new rows. I am using get_or_create as follows:
p, created = Practice.objects.get_or_create(
code=row[1],
name=row[2],
address1=row[3],
address2=row[4],
address3=row[5],
address4=row[6],
postcode=row[7]
)
But when I try to run this I get:
django.db.utils.IntegrityError: duplicate key value violates unique constraint
DETAIL: Key (code)=(A82057) already exists
What's this about? I thought the point of get_or_create was to only try to create new rows if they didn't already exist.
My model looks like this:
class Practice(TimeStampedModel):
code = models.CharField(max_length=6, primary_key=True, db_index=True)
name = models.CharField(max_length=200)
address1 = models.CharField(max_length=200, null=True, blank=True)
address2 = models.CharField(max_length=200, null=True, blank=True)
address3 = models.CharField(max_length=200, null=True, blank=True)
address4 = models.CharField(max_length=200, null=True, blank=True)
postcode = models.CharField(max_length=9, null=True, blank=True)
def __str__(self):
return self.name
class Meta:
app_label = 'frontend'
ordering = ['name']
Is it something to do with the fact that I've set a manual primary key? I can't see anything in the Django docs about this restriction.
get_or_create attempts to do a get with all of the parameters you pass, not just the PK. So if there is an object with a matching PK but a different postcode, for example, the get will fail so a create will be attempted - but, since you have a manual PK, it will try to create a duplicate one using the data you have passed.
Generally speaking using a non-autoincrementing PK is a bad idea. But if you are just trying to look up against the PK only, use the defaults argument:
p, created = Practice.objects.get_or_create(
code=row[1],
defaults={
'name': row[2],
'address1': row[3],
'address2': row[4],
'address3': row[5],
'address4': row[6],
'postcode': row[7]
})