Right now, I have an abstract model with several models that inherits its fields. But I have discovered the power of Proxy models, and I want to implement them to my app. This is a picture from now:
class BaseModel(models.Model):
field_1 = models.CharField(max_length=10)
field_2 = models.CharField(max_length=10)
field_3 = models.CharField(max_length=10)
class Meta:
abstract = True
class Model1(BaseModel):
pass
def __unicode__(self):
return self.field_1
class Model2(BaseModel):
pass
def __unicode__(self):
return self.field_1
And this is what I want:
class BaseModel(models.Model):
field_1 = models.CharField(max_length=10)
field_2 = models.CharField(max_length=10)
field_3 = models.CharField(max_length=10)
class Model1(BaseModel):
pass
class Meta:
proxy = True
def __unicode__(self):
return self.field_1
class Model2(BaseModel):
pass
class Meta:
proxy = True
def __unicode__(self):
return self.field_1
The problem is when I remove the "abstract = True" sentence. When I try to migrate, this is the warning:
You are trying to add a non-nullable field 'basemodel_ptr' to Model1 without a default; we can't do that (the database needs something to populate existing rows).
Ok, got it. I read "ptr" is a pointer to the parent model, which is BaseModel, but I don't have any BaseModel, and I cannot get it until I migrate. How can I fix this??
The migrations for this are not trivial.
Currently, your database has two tables:
yourapp_model1
yourapp_model2
Both have the same columns but they use different sequences meaning that their primary keys clash: Both of the tables will start counting their IDs (aka PKs) from 1 onwards. There is a very good chance that there is an instance of Model1 with pk=1 as well as an instance of Model2 with pk=1 and they are not the same.
This is the point of having an abstract model and concrete implementations: share Django code (business logic) while separating the data in the DB. For example, because it is semantically different (different types).
The point of proxy models is the exact opposite: while the data is located in one table in the DB, the proxy models allow you to implement different behaviour but based on the same DB data.
If you are now migrating from abstract models to proxy models it means that what you once considered different types will now become the same types (from a database point of view). As mentioned at the beginning, you will have to move the data from several tables into one and regenerate the IDs for at least part of it. Meaning also, that part of the resources that are currently using these URLs will change/cease to exist/point to a different resource.
Unless you can start from scratch (no live data that you have to support) you should really appreciate the magnitude of this change.
Should you not have the need to support live data:
drop the database
recreate the database
remove all migration files
recreate all migration files from scratch
call migrate
Data migration of live data:
Note that there are other ways to do this. Search for "Django data migration".
create the new Model structure with new names (no collisions)
makemigrations which will now pick up the new model structure and create new and empty tables in database leaving the old tables and their data untouched
create a management command that reads in the old model instances and copies them into the new tables
once this command has been run on production you can deprecate the old models and remove them while making sure that everything that was depending on them is now using the new tables
I'm trying to accomplish the following:
class DownloadContentFiles(models.Model):
download_content = models.ForeignKey('DownloadContent')
media_file = models.ForeignKey(MediaFile)
class DownloadContent(models.Model):
files = models.ManyToManyField(MediaFile, through=DownloadContentFiles)
class Meta:
abstract=True
I can see why this doesn't work. Because of the abstract on DownloadContent.
Is there a workaround to specify a intermediate model for contenttype fields?
Generally, if you need more informations when creating a field (such as a list of choices) or a concrete Django model (as you do), you can use initialize_type for that.
class DownloadContent(models.Model):
#classmethod
def initialize_type(cls):
cls.add_to_class('files', ... your model field ...)
The MediaFileContent uses this method to add the type selector:
https://github.com/feincms/feincms/blob/master/feincms/content/medialibrary/models.py#L58
However, in your case this does not work because you'd also have to create the through model dynamically. The reason for that is that for each concrete DownloadContent, you'd need another concrete DownloadContentFiles model.
You could achieve this by using the type built-in to dynamically create new DownloadContentFiles concrete classes (beware of name clashes when using DownloadContent with different CMS bases such as page.Page and elephantblog.Entry).
Maybe a simpler way to achieve what you want:
Add a Downloads model somewhere, and add the files ManyToManyField to this class instead
The DownloadContent only contains a ForeignKey(Downloads)
Yes, you need another model. It might be worth it because you can build a better editing interface for Downloads, and the page editor is also simplified because you only have to select one of the already existing Downloads models for showing them on a page.
Maybe explicitely defining class_name to create_content_type could work for you. Something like this:
class DownloadContentFiles(models.Model):
download_content = models.ForeignKey('MyDownloadContent')
media_file = models.ForeignKey(MediaFile)
class DownloadContent(models.Model):
files = models.ManyToManyField(MediaFile, through=DownloadContentFiles)
class Meta:
abstract=True
Page.create_content_type(DownloadContent, class_name="MyDownloadContent")
Wih the following model setup:
class Cat(models.Model):
claw = models.CharField(max_length=20)
name = models.CharField(max_length=20)
class Fur(models.Model):
type = models.CharField(max_length=20)
cat = models.ForeignKey(Cat)
class Meta:
db_table=u'cat_view'
managed=False
Fur has a foreign key to Cat. CatView is a subset view of Cat that is being managed manually. Is there a way to make use of django's useful reverse set methods with this setup?
Additionally, I could just use Fur.objects.filter(cat_id=cat_view.id, ...) which would be the same functionality as cat_view.fur_set.filter(...), however I could not do reverse lookups such as CatView.objects.filter(fur__type="shaggy").
EDIT:
Added example models file, changed image for clarity, added minor complexity to question.
Firstly, neither of those ORM calls you give will work: fur_set is an attribute of a cat instance, not of the Cat.objects Manager.
Secondly, Django has no specific support for database views at all, so your question about using this particular bit of functionality with a view is a bit strange. You could define CatView as a separate (unmanaged) model, although you'd need to be careful about updating and saving. Then you can get the same effect as the reverse relation by querying the Fur object directly:
Fur.objects.filter(cat=my_cat_view.id)
This is a problem concerning django.
I have a model say "Automobiles". This will have some basic fields like "Color","Vehicle Owner Name", "Vehicle Cost".
I want to provide a form where the user can add extra fields depending on the automobile that he is adding. For example, if the user is adding a "Car", he will extra fields in the form, dynamically at run time, like "Car Milage", "Cal Manufacturer".
Suppose if the user wants to add a "Truck", he will add "Load that can be carried", "Permit" etc.
How do I achieve this in django?
There are two questions here:
How to provide a form where the user can add new fields at run time?
How to add the fields to the database so that it can be retrieved/queried later?
There are a few approaches:
key/value model (easy, well supported)
JSON data in a TextField (easy, flexible, can't search/index easily)
Dynamic model definition (not so easy, many hidden problems)
It sounds like you want the last one, but I'm not sure it's the best for you. Django is very easy to change/update, if system admins want extra fields, just add them for them and use south to migrate. I don't like generic key/value database schemas, the whole point of a powerful framework like Django is that you can easily write and rewrite custom schemas without resorting to generic approaches.
If you must allow site users/administrators to directly define their data, I'm sure others will show you how to do the first two approaches above. The third approach is what you were asking for, and a bit more crazy, I'll show you how to do. I don't recommend using it in almost all cases, but sometimes it's appropriate.
Dynamic models
Once you know what to do, this is relatively straightforward. You'll need:
1 or 2 models to store the names and types of the fields
(optional) An abstract model to define common functionality for your (subclassed) dynamic models
A function to build (or rebuild) the dynamic model when needed
Code to build or update the database tables when fields are added/removed/renamed
1. Storing the model definition
This is up to you. I imagine you'll have a model CustomCarModel and CustomField to let the user/admin define and store the names and types of the fields you want. You don't have to mirror Django fields directly, you can make your own types that the user may understand better.
Use a forms.ModelForm with inline formsets to let the user build their custom class.
2. Abstract model
Again, this is straightforward, just create a base model with the common fields/methods for all your dynamic models. Make this model abstract.
3. Build a dynamic model
Define a function that takes the required information (maybe an instance of your class from #1) and produces a model class. This is a basic example:
from django.db.models.loading import cache
from django.db import models
def get_custom_car_model(car_model_definition):
""" Create a custom (dynamic) model class based on the given definition.
"""
# What's the name of your app?
_app_label = 'myapp'
# you need to come up with a unique table name
_db_table = 'dynamic_car_%d' % car_model_definition.pk
# you need to come up with a unique model name (used in model caching)
_model_name = "DynamicCar%d" % car_model_definition.pk
# Remove any exist model definition from Django's cache
try:
del cache.app_models[_app_label][_model_name.lower()]
except KeyError:
pass
# We'll build the class attributes here
attrs = {}
# Store a link to the definition for convenience
attrs['car_model_definition'] = car_model_definition
# Create the relevant meta information
class Meta:
app_label = _app_label
db_table = _db_table
managed = False
verbose_name = 'Dynamic Car %s' % car_model_definition
verbose_name_plural = 'Dynamic Cars for %s' % car_model_definition
ordering = ('my_field',)
attrs['__module__'] = 'path.to.your.apps.module'
attrs['Meta'] = Meta
# All of that was just getting the class ready, here is the magic
# Build your model by adding django database Field subclasses to the attrs dict
# What this looks like depends on how you store the users's definitions
# For now, I'll just make them all CharFields
for field in car_model_definition.fields.all():
attrs[field.name] = models.CharField(max_length=50, db_index=True)
# Create the new model class
model_class = type(_model_name, (CustomCarModelBase,), attrs)
return model_class
4. Code to update the database tables
The code above will generate a dynamic model for you, but won't create the database tables. I recommend using South for table manipulation. Here are a couple of functions, which you can connect to pre/post-save signals:
import logging
from south.db import db
from django.db import connection
def create_db_table(model_class):
""" Takes a Django model class and create a database table, if necessary.
"""
table_name = model_class._meta.db_table
if (connection.introspection.table_name_converter(table_name)
not in connection.introspection.table_names()):
fields = [(f.name, f) for f in model_class._meta.fields]
db.create_table(table_name, fields)
logging.debug("Creating table '%s'" % table_name)
def add_necessary_db_columns(model_class):
""" Creates new table or relevant columns as necessary based on the model_class.
No columns or data are renamed or removed.
XXX: May need tweaking if db_column != field.name
"""
# Create table if missing
create_db_table(model_class)
# Add field columns if missing
table_name = model_class._meta.db_table
fields = [(f.column, f) for f in model_class._meta.fields]
db_column_names = [row[0] for row in connection.introspection.get_table_description(connection.cursor(), table_name)]
for column_name, field in fields:
if column_name not in db_column_names:
logging.debug("Adding field '%s' to table '%s'" % (column_name, table_name))
db.add_column(table_name, column_name, field)
And there you have it! You can call get_custom_car_model() to deliver a django model, which you can use to do normal django queries:
CarModel = get_custom_car_model(my_definition)
CarModel.objects.all()
Problems
Your models are hidden from Django until the code creating them is run. You can however run get_custom_car_model for every instance of your definitions in the class_prepared signal for your definition model.
ForeignKeys/ManyToManyFields may not work (I haven't tried)
You will want to use Django's model cache so you don't have to run queries and create the model every time you want to use this. I've left this out above for simplicity
You can get your dynamic models into the admin, but you'll need to dynamically create the admin class as well, and register/reregister/unregister appropriately using signals.
Overview
If you're fine with the added complication and problems, enjoy! One it's running, it works exactly as expected thanks to Django and Python's flexibility. You can feed your model into Django's ModelForm to let the user edit their instances, and perform queries using the database's fields directly. If there is anything you don't understand in the above, you're probably best off not taking this approach (I've intentionally not explained what some of the concepts are for beginners). Keep it Simple!
I really don't think many people need this, but I have used it myself, where we had lots of data in the tables and really, really needed to let the users customise the columns, which changed rarely.
Database
Consider your database design once more.
You should think in terms of how those objects that you want to represent relate to each other in the real world and then try to generalize those relations as much as you can, (so instead of saying each truck has a permit, you say each vehicle has an attribute which can be either a permit, load amount or whatever).
So lets try it:
If you say you have a vehicle and each vehicle can have many user specified attributes consider the following models:
class Attribute(models.Model):
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
attribute = models.ManyToMany(Attribute)
As noted before, this is a general idea which enables you to add as much attributes to each vehicle as you want.
If you want specific set of attributes to be available to the user you can use choices in the Attribute.type field.
ATTRIBUTE_CHOICES = (
(1, 'Permit'),
(2, 'Manufacturer'),
)
class Attribute(models.Model):
type = models.CharField(max_length=1, choices=ATTRIBUTE_CHOICES)
value = models.CharField()
Now, perhaps you would want each vehicle sort to have it's own set of available attributes. This can be done by adding yet another model and set foreign key relations from both Vehicle and Attribute models to it.
class VehicleType(models.Model):
name = models.CharField()
class Attribute(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
attribute = models.ManyToMany(Attribute)
This way you have a clear picture of how each attribute relates to some vehicle.
Forms
Basically, with this database design, you would require two forms for adding objects into the database. Specifically a model form for a vehicle and a model formset for attributes. You could use jQuery to dynamically add more items on the Attribute formset.
Note
You could also separate Attribute class to AttributeType and AttributeValue so you don't have redundant attribute types stored in your database or if you want to limit the attribute choices for the user but keep the ability to add more types with Django admin site.
To be totally cool, you could use autocomplete on your form to suggest existing attribute types to the user.
Hint: learn more about database normalization.
Other solutions
As suggested in the previous answer by Stuart Marsh
On the other hand you could hard code your models for each vehicle type so that each vehicle type is represented by the subclass of the base vehicle and each subclass can have its own specific attributes but that solutions is not very flexible (if you require flexibility).
You could also keep JSON representation of additional object attributes in one database field but I am not sure this would be helpfull when querying attributes.
Here is my simple test in django shell- I just typed in and it seems work fine-
In [25]: attributes = {
"__module__": "lekhoni.models",
"name": models.CharField(max_length=100),
"address": models.CharField(max_length=100),
}
In [26]: Person = type('Person', (models.Model,), attributes)
In [27]: Person
Out[27]: class 'lekhoni.models.Person'
In [28]: p1= Person()
In [29]: p1.name= 'manir'
In [30]: p1.save()
In [31]: Person.objects.a
Person.objects.aggregate Person.objects.all Person.objects.annotate
In [32]: Person.objects.all()
Out[33]: [Person: Person object]
It seems very simple- not sure why it should not be a considered an option- Reflection is very common is other languages like C# or Java- Anyway I am very new to django things-
Are you talking about in a front end interface, or in the Django admin?
You can't create real fields on the fly like that without a lot of work under the hood. Each model and field in Django has an associated table and column in the database. To add new fields usually requires either raw sql, or migrations using South.
From a front end interface, you could create pseudo fields, and store them in a json format in a single model field.
For example, create an other_data text field in the model. Then allow users to create fields, and store them like {'userfield':'userdata','mileage':54}
But I think if you're using a finite class like vehicles, you would create a base model with the basic vehicle characteristics, and then create models that inherits from the base model for each of the vehicle types.
class base_vehicle(models.Model):
color = models.CharField()
owner_name = models.CharField()
cost = models.DecimalField()
class car(base_vehicle):
mileage = models.IntegerField(default=0)
etc
I've just started using Django's contenttypes framework (and it's pretty awesome). I have an Area object that has many specialised Block objects - I've modeled it as below.
class Area(models.Model):
...
class SomeBlock(models.Model):
...
class AreaBlock(models.Model):
area = models.ForeignKey(Area)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
block_object = generic.GenericForeignKey('content_type', 'object_id')
This works very well. Now I want to register these specialised blocks in their own table along with extra 'global' information about it.
class BlockRegistrar(models.Model):
...
My problem is joining SomeBlock to it's global information in the BlockRegistrar model. A foreignkey in the block table would be obvious, but it wouldn't ever change and I can't figure out how to use django's ContentType table to do these joins.
Any help?
So AreaBlock.block_object is a GenericForeignKey to one of several possible SomeXXBlock models?
Do you mean every SomeXXBlock record has a corresponding BlockRegistrar record?
The easiest way to do that would be with model inheritance:
class BlockRegistrar(models.Model):
# 'global' fields common to all SomeXXBlock models
...
class SomeBlock(BlockRegistrar):
...
class SomeOtherBlock(BlockRegistrar):
...
Behind the scenes Django automatically creates a OneToOneField linking the SomeXXBlock instance to it's corresponding BlockRegistrar. In Django this is called 'multi-table inheritance'. From your question it sounds like this may be what you want.
If you never need to access a BlockRegistrar object directly, but just want the global fields to be available on all the SomeXXBlock 'child' models then you could use an Abstract base model:
class BlockRegistrar(models.Model):
# 'global' fields common to all SomeXXBlock models
...
class Meta:
abstract = True
class SomeBlock(BlockRegistrar):
...
class SomeOtherBlock(BlockRegistrar):
...
In this case the BlockRegistrar model does not exist as a table in the db and there is no magic OneToOneField added to the SomeXXBlock models.