Django model names are case insensitive, right? - python

If I have myapp/models.py
from django.db import models
class FooBar(models.Model):
x = models.BooleanField()
class Foobar(models.Model):
y = models.BooleanField()
and add myapp to INSTALLED_APPS and do a syncdb, I only get FooBar model converted to a db table. The Foobar model is ignored.
Another strange thing to note is that when we do
from myapp import models
both FooBar and Foobar are present as attributes of models. However,
>>> models.FooBar.__name__
'FooBar'
>>> models.Foobar.__name__
'FooBar'
and both are just interfaces to the db table of FooBar (by default myapp_foobar).
I am asking this question because it seems to me from this that django model names are case insensitive and yet I have not found any documentation stating this and moreover this question was answered to the effect that django model names are case sensitive.

Django model names are not case insensitive, but basically, Django creates a lowercase table name from the app and model names. Thus FooBar, which is in myapp, will generate a myapp_foobar table, and so will Foobar.
Obviously in your example, the names will overlap. Django should warn about clashing table names, but instead, silently ignores clashing models.
To fix this, I suggest you have explicit and distinct table names for your two models. Use the Meta inner class, and set Meta.db_table. See the Django documentation on models Meta.
Example given:
class FooBar(models.Model):
x = models.BooleanField()
class Meta:
db_table = 'myapp_foobar_one'
class Foobar(models.Model):
x = models.BooleanField()
class Meta:
db_table = 'myapp_foobar_two'
In a real world environment, I would never have two similar model names in the same app. It is extremely error-prone.

Related

Converting a model to its base model

Consider this file :
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
Now, let's say that I have a Restaurant, named restaurant. But this place is no longer a restaurant, so I want to transform it to a Place. For that, I do :
p = Place.objects.get(pk=place_id)
p.restaurant.delete()
p.save()
It works well, p is no longer a restaurant, but something strange happens : The primary key (ID) of p in the Place table change, like if the Place was deleted and then recreated.
Why is this happening ?
And how can I transform my restaurant to a place without changing the place ID ?
You inherited restaurant from place and Django do some stuff about this kind of relation between two table in DB.Django calls that Multi-table inheritance.
Some of the features of this type of design include the following:
PK of two objects are same.
Two object in Django point of view seems as a one object so every change in PK of one object cause automatic change in other object.
There is an automatically-created OneToOneField relation between two model.
There is atomic transaction for query in these objects.
So this is normal behavior of Django ORM and Django manage change in those two object's PK. You can read more about this concept with concrete model and multi-table inheritance in this link.

Inheritance model update to its parent model

I need extend a model from another model.
Case:
core/models.py
class Master(models.Model):
code = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=100, blank=False, null=False)
class Meta:
abstract = True
class City(Master):
zipcode = models.IntegerField()
custom/models.py
from core.models import City
class City(City)
newfield = models.CharField(max_length=20)
custom is an app.
I have tried with proxy model but it is not what I need, since proxy model adds a new table. https://docs.djangoproject.com/en/2.2/topics/db/models/#proxy-models
I need is that when I migrate add the new field to City.
More info.
In core the table is created and in custom you can add new fields that the client needs. The idea is that core is only maintained as standard.
Proxy models don't add new tables. From the docs link you mentioned:
The MyPerson class operates on the same database table as its parent Person class.
If you want one table called core_city, and another called custom_city, the second one having an extra field, you simply subclass it. Perhaps it would be easier to use an alias:
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
custom_city will have all fields from core_city, plus a newfield. The description of how this works (and an example) is covered in the docs section Multi-table inheritance.
If what you want is to have one single database table, then you should use a proxy Model, however they really don't allow you to create new fields. The field should be created in the parent model, or otherwise exist in the database and not be handled by Django migrations at all.
You are looking for Abstract base classes models:
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class.
This is the base class:
#core/models.py
class City(Master):
zipcode = models.IntegerField()
class Meta:
abstract = True # <--- here the trick
Here your model:
#custom/models.py
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.
You can update or create your class constants after its defined like this
from core.models import City
City.newfield = models.CharField(max_length=20)
You may need to use swappable models, using them you can define a City class and change it with whichever model you need later,
but that way you can't import and use the base City model directly, you may need to provide a method like get_city_model for that, as your public API.
class City(Master):
zipcode = models.IntegerField()
class Meta:
swappable = 'CORE_CITY_MODEL'
and maybe replace it later with some other model, then just set CORE_CITY_MODEL to that model in the form of 'app_name.model_name'.
The django.contrib.auth is a good example of this, you may consider checking User model and get_user_model method. Although I think you may face problems if you change your city model after you did run migrate, it may not move your data to the new table, but I'm not sure about this.

How to find all Django foreign key references to an instance

How do you find all direct foreign key references to a specific Django model instance?
I want to delete a record, but I want to maintain all child records that refer to it, so I'm trying to "swap out" the reference to the old record with a different one before I delete it.
This similar question references the Collector class. I tried:
obj_to_delete = MyModel.objects.get(id=blah)
new_obj = MyModel.objects.get(id=blah2)
collector = Collector(using='default')
collector.collect([obj_to_delete])
for other_model, other_data in collector.field_updates.iteritems():
for (other_field, _value), other_instances in other_data.iteritems():
# Why is this necessary?
if other_field.rel.to is not type(first_obj):
continue
for other_instance in other_instances:
setattr(other_instance, other_field.name, new_obj)
other_instance.save()
# All FK references should be gone, so this should be safe to delete.
obj_to_delete.delete()
However, this seems to have two problems:
Sometimes collector.field_updates contains references to models and fields that have nothing to do with my target obj_to_delete.
My final obj_to_delete.delete() call fails with IntegrityErrors complaining about remaining records that still refer to it, records that weren't caught by the collector.
What am I doing wrong?
I just need a way to lookup all FK references to a single model instance. I don't need any kind of fancy dependency lookup like what's used in Django's standard deletion view.
You can use Django's reverse foreign key support.
Say you have two models, like so:
class Foo(models.Model):
name = models.CharField(max_length=10)
class Bar(models.Model):
descr = models.CharField(max_length=100)
foo = models.ForeignKey(Foo)
Then you know you can do bar_instance.foo to access the Foo object it keys to. But you can use the reverse foreign key on a Foo instance to get all the Bar objects that point to it using, e.g, foo.bar_set.
Personally, I think the best option is to avoid the cascaded deletion.
Declaring the foreign keys in the related models with the proper Django option, e.g.
on_delete=models.SET_NULL
should suffice.
Borrowing the sample models from #Joseph's answer:
class Foo(models.Model):
name = models.CharField(max_length=10)
class Bar(models.Model):
descr = models.CharField(max_length=100)
foo = models.ForeignKey(Foo, blank=True, null=True, on_delete=models.SET_NULL))
As described in the official Django docs, here are the predefined behaviours you can use and experiment with:
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:
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0]
class MyModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.SET(get_sentinel_user))
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

django models relation to two models before saving

I have two apps say app1 and app2 and I have models in it.
from app2.models import SecondModel
class FirstModel(models.Model):
first_field = models.ManyToManyField(SecondModel, blank=True)# or Foreign Key
from app1.models import FirstModel
class SecondModel(models.Model):
second_field = models.ForeignKey(FirstModel)
When I do this I get import error.
Could not import name 'FirstModel'
Why is this happening ?
The error is because you have a circular import. It's not possible to for both modules to import from each other.
In this case, you don't need to import the models into each app. Remove the imports, and use a string app_label.ModelName instead.
# app1.models.py
class FirstModel(models.Model):
first_field = models.ManyToManyField('app2.SecondModel')
# app2.models.py
class SecondModel(models.Model):
second_field = models.ForeignKey('app1.FirstModel')
there is a name conflict here .. you defined the FirstModel in your models.py and then defined FirstModel, from the code above, this could be the possible problem. Also, the import error generally mean, there is no FirstModel defined from where you are importing it.
However, a more generic way of doing FKs without import is generally
class FkModel(models.Model):
relationship = models.ManyToManyField('appName.modelName')
where appName is the app from where you are trying to import the model from, and modelName is the model to which you are trying to create the relationship. This helps where you are trying to do something like this.
Lets say your app name is 'app' and you are trying to create a many to many relationship from 1st model to a 2nd model for which the class is declared after the 1st model e.g.
class Model1(models.Model):
first_field = models.ManyToManyField('app.Model1')
class Model2(models.Model):
name = models.CharField(maxlength=256)
that is just put your appname.modelName inside strings :)
also, you have a flaw in your ManyToManyField() declaration i.e. you don't need to define blank in Many to Many. The way db's work under the hood is, they create a 3rd database table just to store many to many relationships.
hope it helps
//mouse.

Using Django South to move from concrete inheritance to abstract inheritance

I have an existing Django project that has several models using concrete inheritance of a base class. After closer consideration, and after reading about what people like Jacob Kaplan-Moss have to say about it, using this concrete inheritance is unnecessary in my case. I would like to migrate to using an abstract base class instead.
The thing that makes this complicated is that my site is live and I have user entered data. Thus, I'll need to keep all my data intact throughout this transition.
I'll give an example to be more concrete:
Before:
app1/models.py:
class Model1(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
app2/models.py:
class Model2(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
base_app/models.py:
class BaseModel(models.Model):
user = models.ForeignKey(User)
another_field = models.CharField(max_length=1000)
After:
app1/models.py:
class Model1(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
app2/models.py:
class Model2(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
base_app/models.py:
class BaseModel(models.Model):
user = models.ForeignKey(User)
another_field = models.CharField(max_length=1000)
class Meta:
abstract = True
Right now, my plan is to first add the abstract = True to the BaseModel. Then,for each model that uses BaseModel, one at a time:
Use south to migrate the database and create this migration using the --auto flag
Use a south data migration. For instance, I would loop through each object in Model1 to fetch the object in BaseModel that has the same pk and copy the values for each field of the BaseModel object to the Model1 object.
So first, will this work? And second, is there a better way to do this?
Update:
My final solution is described in detail here:
http://www.markliu.me/2011/aug/23/migrating-a-django-postgres-db-from-concrete-inhe/
Add NewBaseModel, we use different name so it doesn't conflict with current non-abstract one (South would actually delete BaseModel otherwise).
class NewBaseModel(models.Model):
user = models.ForeignKey(User)
another_field = models.CharField(max_length=1000)
class Meta:
abstract = True
Set Model1 and Model2 to inherit from NewBaseModel
Run schemamigration --auto, 2 new fields will be added to Model1 and Model2
Run datamigration --empty and fill new fields from values in BaseModel
Load production db and double check everything migrated correctly
Remove BaseModel and rename NewBaseModel to BaseModel
Run schemamigration --auto (this should work ;) )
Deploy!
NOTE: Use orm variable when migrating to use current state of your model schema.
Sebastjan TrepĨa's answer is probably good but, another way to do it will be to create your migration manually:
Add the abstract = True to your base model.
Run schemamigration --auto, the generated migration will probably not be good but you will use it as a base.
Edit the migration file. In the forward you should add, in this order:
a. db.delete_foreign_key(table_name, column) for each of your children models. This will remove the ForeignKey between the parent and the children table.
b. db.delete_table(BaseModel) to delete the table of the base model (this command should be probably there already, generated by --auto).
c. It's possible that you will have to rename all the primary key column of your children models to 'id'. I'm not sure about this. If you need to do this: db.rename_column(table_name, column_name, 'id') for each of your children models.
Remove all auto-generated code in forward that doesn't make sense.
Run the migration: migrate
What this method is doing is removing the table of the base class and the foreign keys between the base class table and its children because they are not use with the Abstract Base Class.
I didn't test this method so it's possible that you'll hit some problems. This approach is more complicated then the other one but the advantages are that you don't need to migrate the data and that you will understand what is happening. It should run also pretty fast, a good thing for a live migration.
You can consult the South API for more info.
One really important thing, in any method you will use, work on a local copy of your system and database. When you will be really sure that the migration is working well, backup your production DB then apply your migration and then restart your webserver (to load your modified model code).

Categories

Resources