Mixin Field into Existing and uneditable django model - python

I would like to mix a field into an existing model which I would rather not edit (it comes from a third party project and I would rather leave the project untouched). I have created a simple example which illustrates what I am trying but unable to do:
In an empty Django project I have created apps app1 and app2 (they are in that order in settings). They look like the following:
app1.models.py:
from django.db import models
from app2.models import BlogPost
class BlogPostExtend(models.Model):
custom_field_name = models.CharField(max_length=200)
class Meta:
abstract = True
BlogPost.__bases__ = (BlogPostExtend,)+BlogPost.__bases__ # this prevents MRO error
app2.models.py:
from django.db import models
class BlogPost(models.Model):
field_name = models.CharField(max_length=100)
Unfortunately this does not result in custom_field_name being created in the database when I syncdb, although at the command line if I type BlogPost.custom_field_name it does recognize it as a CharField. I know that in this simple case I could have BlogPost inherit from BlogPostExtend, but in the real use case I cannot edit BlogPost.
This is a very simplified example but it illustrates what I am trying to do.
Thanks!

Mixins work great with adding attributes and methods, but not fields.
In app1.models.py, do this instead:
from django.db import models
from app2.models import BlogPost
custom_field_name = models.CharField(max_length=200)
custom_field_name.contribute_to_class(BlogPost, "custom_field_name")
I think also the app1 app should come after app2 in INSTALLED_APPS for this to work.
Here is an explanation on contribute_to_class

Related

How to dynamically make an existing non-abstract django model, abstract?

I think I have a more or less unorthodox and hackish question for you. What I currently have is django project with multiple apps.
I want to use a non-abstract model (ModelA) of one app (app1) and use it in another app (app2) by subclassing it. App1's models
should not be migrated to the DB, I just want to use the capabilities of app1 and it's model classes, by extending its functionality and logic.
I achieved that by adding both apps to settings.INSTALLED_APPS, and preventing app1's models being migrated to the DB.
INSTALLED_APPS += (
'App1',
'App2',
)
# This is needed to just use App1's models
# without creating it's database tables
# See: http://stackoverflow.com/a/35921487/1230358
MIGRATION_MODULES = {
'App1': None,
}
So far so good, ugly and hackish, I know... The remaining problem is now that most of app1's models are non-abstract (ModelA) and if I try
to subclass them, none of the ModelA's fields get populated to the db into the table named app2_modelb. This is clear to me, because I excluded the App1 from
migrating to the DB and therefore the table app1_modela is completely missing in the DB.
My idea now was to clone ModelA, preserve all its functionallity, and changing it's Meta information from non-abstract to abstract (ModelB.Meta.abstract = True).
I hope that by this, all the original fields of ModelA will be inherited to ModelB and can be found in its respective DB table and columns (app1_modelb).
What I have right now is:
# In app1 -> models.py
class ModelA(models.Model):
title = models.CharField(_('title'), max_length=255)
subtitle = models.CharField(_('subtitle'), max_length=255)
class Meta:
abstract = False # just explicitly for demonstration
# In app2 -> models.py
from app1.models import ModelA
class ModelB(ModelA):
pass
# Just extending ModelAdoes not create the fields title and subtitle fields in app2_modelb
# because ModelA.meta.abstract = False
My current way (pseudo code) to make an existing non-abstract model abstract looks like this:
# In app2 -> models.py
from app1.models import ModelA
def get_abstract_class(cls):
o = dict(cls.__dict__)
o['_meta'].abstract = True
o['_meta'].app_label = 'app2'
o['__module__'] = 'app2.models'
#return type('Abstract{}'.format(cls.__name__), cls.__bases__, o)
return type('Abstract{}'.format(cls.__name__), (cls,), o)
ModelB = get_abstract_class(ModelA)
class ModelC(ModelB):
# title and subtitle are inherited from ModelA
description = models.CharField(_('description'), max_length=255)
This does not work, and after this lengthy description my (simple) question would be, if and how is it possible to clone a non-abstract model class preserving all its functionality and how to change it to be abstract?
Just to be clear. All upper fuzz is about, that I can't change any code in app1. May it be that app1 is a django app installed via pip.
Why not, in app1
AbstractBaseModelA(models.Model):
# other stuff here
class Meta:
is_abstract=True
ModelA(AbstractBaseModelA):
# stuff
in app2:
MobelB(AbstractBaseModelA):
# stuff
Sorry if I've misunderstood your aims, but I think the above should achieve the same end result.

Collecting and data recording about successors of abstract model

My app_templ models definition:
models.py
class TableName(models.Model):
name = models.CharField(max_length=100)
#
class TableAbstract(models.Model):
...
class Meta:
abstract = True
It can be used by other apps:
app1 / models.py
from app_templ.models import TableAbstract
class Table1(TableAbstract):
...
app2 / models.py
from app_templ.models import TableAbstract
class Table2(TableAbstract):
...
and so on...
It is necessary for me that in TableName, names of models (tables) of successors registered.
How to make it by means of coding only in the app_templ app?
Technically, what you are describing sounds fine. You are defining an abstract model and then using it to create several models. You do need to import it, and to specify that you want to create these tables (using your above examples). You should think carefully about why you are using the same model multiple times in different apps (should this actually be one app?), but in theory it is fine.
I don't quite understand your first definition, you should probably define your model something like this:
class TableBaseClass(models.Model):
name = models.CharField(max_length=100)
class Meta:
abstract = True
abstract = True will mean that the model is not created in your database (docs) so for clarity, you could store this file in a location distinct from your regular model classes that create tables.
This code:
from app_templ.models import TableAbstract
class Table1(TableAbstract):
...
should be in models.py in your app

How to make django admin site to not recognize a foreign key'ed model (on different app) to be a model of current app?

I have three models - two of them are in one app, and the third one is on the another. The structure is like this:
taapp.models:
class Teachers(model.Model):
fullname = models.CharField(max_length=50)
...
class TeachersScale(model.Model):
teacher = models.ForeignKey("Teachers")
abbr = models.ForeignKey("questions.QuestionTypes")
questions.models:
class QuestionTypes(models.Model):
abbr = models.CharField(max_length=5)
......
I registered all these models to admin:
taapp.admin:
from taapp.models import Teachers
from taapp.models import TeachersScale
from django.contrib import admin
from admin_forms import TeachersAdmin, TeachersScaleAdmin
admin.site.register(Teachers, TeachersAdmin)
admin.site.register(TeachersScale, TeachersScaleAdmin)
taapp.admin_forms:
from django import forms
from django.contrib import admin
class TeachersAdmin(admin.ModelAdmin):
list_display = ('fullname', 'email', 'registration_date')
class TeachersScaleAdmin(admin.ModelAdmin):
list_display = ('teacher', 'abbr')
list_filter = ['teacher','abbr']
When I try to add a field to TeachersScale in admin site, I get the following error:
DatabaseError at /admin/taapp/teachersscale/add/
(1146, "Table 'taapp.questions_questiontypes' doesn't exist")
It treats QuestionTypes, as it is a model in taapp. How to solve it? Or is there something wrong with my db design?
I tried TabularInline for QuestionTypes to see if reverse adding works. Well, it works:
questions.admin:
class TeachersScaleInline(admin.TabularInline):
model = TeachersScale
class QuestionTypesAdmin(admin.ModelAdmin):
inlines = [TeachersScaleInline]
Thanks in advance.
It looks like you haven't actually created your questions table, or if you have you've forced it into a different database. Foreign keys expect to share the same database, and it's perfectly standard to have multiple apps sharing the same database. That's why the app name is part of the automatically generated table name.

Getting error with django-taggit with admin

In a Django project I installed django_taggit. I'm getting this error when I syncdb my project.
AttributeError: 'TaggableManager' object has no attribute 'related'
My models.py something like this...
from taggit.managers import TaggableManager
class Post(models.Model):
title = models.CharField(max_length=100)
tags = TaggableManager()
and admin.py something like this...
from django.contrib import admin
admin.site.register(Post)
Django admin is trying to use the TaggableManager to manage your post objects. You need to be careful when using custom managers; as the docs specify:
If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they’re defined in the model) has a special status. Django interprets the first Manager defined in a class as the “default” Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. As a result, it’s a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_query_set() results in an inability to retrieve objects you’d like to work with.
An easy way to get around this is to manually specify Post.objects first:
class Post(models.Model):
title = models.CharField(max_length=100)
objects = models.Manager()
tags = TaggableManager()

How to add a Model (not an app) to django?

I am using django 1.3 and just trying to add a simple model(not an app) to the admin site.
so i have tried:
from django.db import models
class UserProfile(models.Model):
notes = models.TextField(db_name="Notes", max_length=15)
def _str_(self):
return self.notes
class Admin:
pass
and have also tried creating the admin.py file in the site root and in the /static/admin/ directory and have attempted two standard entries for it as follows:
from django.contrib import admin
from mock.models import UserProfile
admin.site.register(UserProfile)
and
from django.contrib import admin
from mock.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin):
pass
admin.site.register(UserProfile, UserProfileAdmin)
any help would be much appreciated. Thank you very much.
Don't define your Admin class in the model itself like below. That's the really old way to do it, from before Django 1.0. I'm not sure what tutorial or documentation you are using, but it's very out of date.
class UserProfile(models.Model):
notes = models.TextField(db_name="Notes", max_length=15)
# don't do this!
class Admin:
pass
Defining a UserProfileAdmin is the correct approach. The admin.py file should not go in the /static/admin/. The static directory is for static files like CSS stylesheets and javascript files, not for Django code.
As for your question of whether you can define a model without defining an app, it's not a really good idea. Lots of parts of django assume that each model belongs to an app. For example the database table name is appname_modelname.
Creating an app doesn't take too long. Run the startapp command and it will create the base directory and files.
./manage.py startapp <appname>
All you then need to do is add the new app to INSTALLED_APPS, and create your admin.py file.
As your project gets bigger, keeping models in apps will keep it more organized. Many Django users create an app named utils (or similar) for the odd model.

Categories