Django making migrations - python-3.x

Consider the following three classes:
class UUIDModel(models.Model):
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
class Meta:
abstract = True
class TimeStampedUUIDModel(UUIDModel):
created = models.DateTimeField(auto_now_add=True, editable=False)
modified = models.DateTimeField(auto_now=True, editable=False)
class Meta:
abstract = True
class UsefulClass(TimeStampedUUIDModel):
name = models.CharField(max_length=150, unique=True)
creator = models.ForeignKey('OtherClass', on_delete=models.SET_NULL, null=True)
Based on these classes, when I am running the makemigrations command, Django will create two migrations (on the very first run). One contains the id, created, modified and name (!) fields and the second one adds the creator foreign key.
What could be the reason of the creation of two migrations, instead of just one?

#PrabinSapal is correct, you could reset your database using python manage.py reset_db --noinput. Django command that resets your Django database, removing all data from all tables. This allows you to run all migrations again.

Related

Django ORM query with user defined fields

I'm trying to create an Django ORM query to replace a really messy raw SQL query i've written in the past but i'm not sure if Django ORM can let me do it. I have three tables:
contacts
custom_fields
custom_field_data
What I'm hoping to be able to do with the ORM is create an output as if i've queryied one single table like this:
Is such a thing possible with the ORM?
EDIT:
The models are:
class Contact(models.Model):
company = models.ForeignKey(Company, on_delete=models.PROTECT, null=True, blank=True)
class CustomField(models.Model):
name = models.CharField(max_length=100)
company = models.ForeignKey(Company, on_delete=models.PROTECT, null=False, blank=False)
class ContactCustomFieldValue(models.Model):
custom_field = models.ForeignKey(CustomField, on_delete=models.PROTECT, related_name='contact_values')
contact = models.ForeignKey(Contact, on_delete=models.PROTECT, related_name='custom_field_values', null=True)
value = models.TextField(null=True)
Solved this one in large part thanks to Ken from the Django forums.
The solution he provided looked like this:
subquery1 = Subquery(ContactCustomFieldValue.objects.filter(contact=OuterRef('id'), custom_field_id=1).values_list('value'))
subquery2 = ...
subquery3 = ...
subquery4 = ...
contact_list = Contact.objects.filter(...).annotate(field1=subquery1, ...)
I've built on it to fit my needs but as a starting point this was perfect

Django: Abstract base class for managing db_table

I'm trying to build my second django (and python for that matter, my first project being the django tutorial :)) project. since this is supposed to be something real i'd like to be thorough and build a good code structure before i go into the meat of the project.
I have a couple of simple models like this
class Task(models.Model):
name = models.CharField(max_length=50, null=False)
description = models.CharField(max_length=255, null=True)
dueDate = models.DateTimeField()
I'm using PostgreSQL and i set up my models to use the app label as a database schema by defining the meta class of each model like this
class Meta:
managed = True
db_table = 'app_name\".\"modelname'
This works well. But i have to do this for every single model.
I want to keep it DRY though. So what i'm trying to do now is to have an abstract base class that does this automatically
so i tried the following:
class SchemaModel(models.Model):
class Meta():
abstract = True
managed = True
db_table = AppConfig.label+'\".\"'+self.__class__.lower()+'s'
(the base class was then inherited of course and i took the nested Meta class out of the normal models)
this didn't work though because self isn't accessible in Meta
after consulting the documentation i tried this:
class SchemaModel(models.Model):
class Meta():
abstract = True
managed = True
db_table = '%(app_label)\".\"%(class)s'
which lead to the property db_table of every model being "%(app_label)\".\"%(class)s"
>>> t = Task()
>>> t._meta.db_table
'%(app_label)"."%(class)s'
>>>
I didn't find anything similar on the internet. Am i trying to do something impossible or "forbidden"?
Solution
The solution is as shown in elyas answer to set the db_table property at the end of models.py by looping through all __subclasses__()
for model in SchemaModel.__subclasses__():
db_table = '{}s'.format(model._meta.label_lower.replace('.','\".\"'))
model._meta.original_attrs['db_table'] = db_table
model._meta.db_table = db_table
I wouldn't say it's forbidden. But I can't think of any way to do this declaratively. There is a way to do it however.
Firstly, on your existing attempts:
Accessing 'self'
db_table = AppConfig.label+'\".\"'+self.__class__.lower()+'s'
An instance object is never created from the Meta class when models are loaded, and so there is no self to reference. But even if an instance object were created, db_table is an attribute of the class object, so it is evaluated when the class object is created, which is before any instance object is created, so self cannot be accessed when defining a class attribute in this way.
Edit: And as you mentioned, the app_label cannot be accessed through AppConfig.label.
String formatting
db_table = '%(app_label)\".\"%(class)s'
These placeholders are only used in a very specific situation when defining the related_name and related_query_name attributes of ForeignKey or OneToOneField's fields in an abstract base class.
A solution
As I said, I can't think of any declarative way to achieve this. For example, trying to use __qualname__ won't work because you would just end up with SchemaModel.Meta every time.
But you could put a for loop at the bottom of your models.py like this:
for model in SchemaModel.__subclasses__():
# Name your db_table here
db_table = model._meta.app_label + '\".\"' + model._meta.model_name.lower() + 's'
# Set db_table here
model._meta.original_attrs['db_table'] = db_table
model._meta.db_table = db_table
All of SchemaModel's children can be found using the built-in __subclasses__() method.
The db_table attribute needs to be updated in two places. Firstly in _meta, which is (partly) created by copying attributes from the Meta class, and secondly in _meta.original_attrs where the original Meta attributes are stored and are read by Django during migrations.
Alternative solution
Personally I would define the db_table names manually and simply have a unit test that checks that all models adhere to whatever naming convention I've come up. I prefer this so if another developer eyeballs a model they have never seen before they can get the full picture based on the declarations in the model (and the abstract base class) and don't get confused about an operation modifying them elsewhere.

Merge two redundant models in Django

I'm working on an online shop at the moment. The shop is written in Django, and was programmed by another person, so I spent days trying to make sense of what he did. At the moment the shop sells only two articles, on two different pages (meaning they have to be bought separately), so the shop is heavily oriented towards this situation. The problem is, the shop's owner expressed his interest in selling everything in one page in the near future, and in adding more products. And that's where my problems start.
The model structure was something like this (extremely simplified):
class Category1(BaseModel):
name = models.CharField(max_length=32)
class Category2(BaseModel):
name = models.CharField(max_length=64)
class Price1(BaseModel):
category = models.ForeignKey(Category1, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=16, decimal_places=2)
currency = models.CharField(max_length=3)
class Price2(BaseModel):
category = models.ForeignKey(Category2, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=16, decimal_places=2)
currency = models.CharField(max_length=3)
class Order1(BaseModel):
[personal information fields]
class Order2(BaseModel):
[personal information fields]
class Article1(BaseModel):
price = models.ForeignKey(Price1, on_delete=models.CASCADE)
order = models.ForeignKey(Order1, on_delete=models.CASCADE, related_name='articles')
class Article2(BaseModel):
price = models.ForeignKey(Price2, on_delete=models.CASCADE)
order = models.ForeignKey(Order2, on_delete=models.CASCADE, related_name='articles')
There is much more than this, of course, but this should be enough to show the relationships between the models. The complete structure of course makes more sense than this one. BaseModel is a class that contains an ID, creation time and last edit.
I managed to put all the common elements into abstract classes BaseCategory, BasePrice, BaseOrder and BaseArticle, but this is not enough if I want to really expand the shop. Finishing this work is just a matter of time and patience, but how should I proceed once I'm in this situation?
class BaseCategory(BaseModel):
name = models.CharField(max_length=64)
class Meta:
abstract = True
class Category1(BaseCategory):
pass
class Category2(BaseCategory):
pass
class BasePrice(BaseModel):
price = models.DecimalField(max_digits=16, decimal_places=2)
currency = models.CharField(max_length=3)
class Meta:
abstract = True
class Price1(BasePrice):
category = models.ForeignKey(Category1, on_delete=models.CASCADE)
class Price2(BasePrice):
category = models.ForeignKey(Category2, on_delete=models.CASCADE)
class BaseOrder(BaseModel):
[personal information fields]
class Meta:
abstract = True
class Order1(BaseOrder):
pass
class Order2(BaseOrder):
pass
class BaseArticle(BaseModel):
class Meta:
abstract = True
class Article1(BaseArticle):
price = models.ForeignKey(Price1, on_delete=models.CASCADE)
order = models.ForeignKey(Order1, on_delete=models.CASCADE, related_name='articles')
class Article2(BaseArticle):
price = models.ForeignKey(Price2, on_delete=models.CASCADE)
order = models.ForeignKey(Order2, on_delete=models.CASCADE, related_name='articles')
I need to get rid of the specific classes completely, otherwise when I will add new articles, I will have to create new classes, and this is not a scalable solution.
My problems are the following:
How do I get rid of the empty specific classes like Price1 or Order1 without losing any information? I know I will have to get rid of the abstract variable, but I don't know what to do next.
How do I manage the foreign keys in the remaining classes? I'm experimenting a bit with GenericForeignKey at the moment, and this would probably let me move the declarations into the base classes, but I'm not sure if changing a definition will reset all fields.
Just to be clear, the shop is already up and running. We can't stop it, and we can't lose data. We sell services, so the customers have to be able to access their products even long after the purchase.
Thanks in advance for your interest and your time.
To keep this answer short we will only discuss one model here. In this instance Category. Firstly add a new model Category (keep your other models for now):
class Category(BaseModel):
name = models.CharField(max_length=64)
Next run makemigrations this would generate a migration file to make this new table in the database. After this you need to make a Data Migration [Django docs] to copy the data from the other two tables that you have.
To do this first run:
python manage.py makemigrations --empty yourappname
This will generate a migration file that does nothing for now. We will edit this migration file and add some code to copy the data from your other tables to this new table. In the end your migration file would look something like:
from django.db import migrations
def populate_category(apps, schema_editor):
Category1 = apps.get_model('yourappname', 'Category')
Category2 = apps.get_model('yourappname', 'Category')
Category = apps.get_model('yourappname', 'Category')
# add all fields except the pk in values(), i.e. values('field1', 'field2')
for category in Category1.objects.values('name'):
Category.objects.create(**category) # Add some field indicating this object is of Category1 if needed
for category in Category2.objects.values('name'):
Category.objects.create(**category) # Add some field indicating this object is of Category2 if needed
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(populate_category, reverse_code=migrations.RunPython.noop),
]
Now you can simply run python manage.py migrate and you would have a new table Category which has all the data from Category1 and Category2. (This might take some time if there are many rows). After this you can remove the models Category1 and Category2 and migrate again to remove those tables.
Note: Perform these operations carefully, and make sure you have got the data properly in the new table before deleting the old ones.
Refer the documentation linked above for more information on
migrations. (Test this on a local development server before doing it
on production to be safe)

DJANGO: How to create a method that copies an object of a model?

I want to create a method that copies an object of the BlogPost model. my models looks like this:
class BlogPost(models.Model):
title = models.CharField(max_length=250)
body = models.TextField()
author = models.ForeignKey(Author, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True)
def copy(self):
pass
class Comment(models.Model):
blog_post = models.ForeignKey(
BlogPost, on_delete=models.CASCADE, related_name="comments"
)
text = models.CharField(max_length=500)
my challenge is implementing the copy method. The copy method should copy the current object with all the comments related to that and I don't have any idea how to do it.
Do you mean you want to save a copy to the database. If so you can do this:
class MyModel(models.Model):
...
def copy(self):
self.id = None
self.save()
The way this works is that if a model instance doesn't have an id, then when you call it's save method, django adds one and creates a new row in your table.

SystemCheckError: System check identified some issues:

I am building a project to place an order. When I run makemigrations command then it gives an error of
SystemCheckError: System check identified some issues and I have deleted the migration file from migrations
Traceback:
order.Order.price: (fields.E304) Reverse accessor for 'Order.price' clashes with reverse accessor for 'Order.product'.
HINT: Add or change a related_name argument to the definition for 'Order.price' or 'Order.product'.
order.Order.product: (fields.E304) Reverse accessor for 'Order.product' clashes with reverse accessor for
'Order.price'.
HINT: Add or change a related_name argument to the definition for 'Order.product' or 'Order.price'.
My models.py is as follows:
class Order(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.ForeignKey(Product, on_delete=models.CASCADE)
def __str__(self):
return self.company + self.product + self.price
Add related_name like:
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='company')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='product')
price = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='price')
related_name is used to link the models. Especially, when you have more than 1 model linking to another model. It is compulsary as in your case. See_this_answer to understand properly about use of related_name.
Both your foreign keys referrencing same model Product without defining different related names:
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.ForeignKey(Product, on_delete=models.CASCADE)
If that's not an error then simply add different related names

Resources