How to override models defined in installed apps in Django? - django

Is it possible to redefine a model used in an INSTALLED_APP without modifying the app in question? For example, django-basic-blog has a Post model which I would like to add a field to. I could edit django-basic-blog directly but for code portability I'd like to build on top of it. I don't want to subclass as I want to preserve all existing references to the Post model. Thanks in advance!

If you subclass the original fields will be still stored in the original table, so references would stay valid.
If you want to monkey-patch an existing class, which is mostly not the recommendable dirty method, you could use contribute_to_class in some models.py file that will be loaded in an app after the one you want to modify:
models.py:
from django.db.models import CharField
from blog.models import Post
CharField(max_length="100").contribute_to_class(Post, 'new_field')
If you do it like this, you always have to bare the risk that your changes can clash with other pieces of code and that your code will be harder to maintain!

Related

Adding cache.clear() to global save in Django

Hopefully somebody knows a simple way to do this.
Is it possible to run the cache.clear() function on every save to any model?
I know Django docs show you how to implement per model, and overhead isn't an issue. I would like to clear the cache each and every time a change is made.
There are a couple of ways you could do this.
Firstly you could create a common abstract base class that inherits from models.Model and overrides save to do the cache clear, then make all your models inherit from that.
Another way might be to use signals - the pre and post save signals have sender as an optional argument, if you don't specify a particular model it will fire for all models.
#Daniel Rosemen. Sorry as I don't have much reputation I couldn't remove my comment.
Thanks for pointing me in the right direction.
It was as simple as:
from django.db.models.signals import post_save
from django.core.cache import cache
#receiver(post_save)
def clear_the_cache(**kwargs):
cache.clear()

Get the ORM that is created by Django

From the Django docs:
How are the backward relationships possible?
Other object-relational mappers require you to define relationships on
both sides. The Django developers believe this is a violation of the
DRY (Don’t Repeat Yourself) principle, so Django only requires you to
define the relationship on one end.
But how is this possible, given that a model class doesn’t know which
other model classes are related to it until those other model classes
are loaded?
The answer lies in the INSTALLED_APPS setting. The first time any
model is loaded, Django iterates over every model in INSTALLED_APPS
and creates the backward relationships in memory as needed.
Essentially, one of the functions of INSTALLED_APPS is to tell Django
the entire model domain.
Is there a way to get this ORM model? I am trying to debug some reverse relations that are not automagically created and it would really help to see the whole ORM Django has created.
There is no specific ORM "Model", however there are a few things that may help you.
from django.db.models.loading import get_models
get_models() will return you a list of every registered model, this list is what the mechanism that you are describing loops over.
YourModel._meta.get_all_related_objects_with_model()
This function loops over every field in every registered model and finds and returns any reverse relations to your YourModel.
The Options class from django.db.models.options (YourModel._meta is an Options object) is a good place to look around for this stuff.
Django doesn't "create" an ORM so the question makes no sense. If you want to know what properties the ORM adds to your model classes to support backward relationships then you can
open a django shell, import your models and inspect them
read the source code (hey, it's open source isn't it ?)
if that's not enough, add breakpoints at appropriate places and run the whole thing thru the debugger

How do I change field or model attributes of a third-party Django app?

Let's say, I use django.contrib.flatpages, or any other third-party app in my project. I want to change some of this app's model attributes - for example, verbose_name.
How can I do that?
The simple answer is "don't"; use a proxy model instead.
It depends. If you want to add some new fields, you can create another model with OneToOneField. If you want to add some methods, ordering etc., use proxy model. If you want to change some DB restrictions (e.g. max_length) you can patch the source code of the app, but think twice before doing that, you should have a really good reason for that.
If you want to change verbose_name, you can override label in corresponding form field, no change in model needed.

Django: How to dynamically add tag field to third party apps without touching app's source code

Scenario: large project with many third party apps. Want to add tagging to those apps without having to modify the apps' source.
My first thought was to first specify a list of models in settings.py (like ['appname.modelname',], and call django-tagging's register function on each of them. The register function adds a TagField and a custom manager to the specified model. The problem with that approach is that the function needs to run BEFORE the DB schema is generated.
I tried running the register function directly in settings.py, but I need django.db.models.get_model to get the actual model reference from only a string, and I can't seem to import that from settings.py - no matter what I try I get an ImportError. The tagging.register function imports OK however.
So I changed tactics and wrote a custom management command in an otherwise empty app. The problem there is that the only signal which hooks into syncdb is post_syncdb which is useless to me since it fires after the DB schema has been generated.
The only other approach I can think of at the moment is to generate and run a 'south' like database schema migration. This seems more like a hack than a solution.
This seems like it should be a pretty common need, but I haven't been able to find a clean solution.
So my question is: Is it possible to dynamically add fields to a model BEFORE the schema is generated, but more specifically, is it possible to add tagging to a third party model without editing it's source.
To clarify, I know it is possible to create and store Tags without having a TagField on the model, but there is a major flaw in that approach in that it is difficult to simultaneously create and tag a new model.
From the docs:
You don't have to register your models
in order to use them with the tagging
application - many of the features
added by registration are just
convenience wrappers around the
tagging API provided by the Tag and
TaggedItem models and their managers,
as documented further below.
Take a look at the API documentation and the examples that follow for how you can add tags to any arbitrary object in the system.
http://api.rst2a.com/1.0/rst2/html?uri=http://django-tagging.googlecode.com/svn/trunk/docs/overview.txt#tags
Updated
#views.py
def tag_model_view(request, model_id):
instance_to_tag = SomeModel.objects.get(pk=model_id)
setattr(instance_to_tag, 'tags_for_instance', request.POST['tags'])
...
instance_to_tag.save()
...returns response
#models.py
#this is the post_save signal receiver
def tagging_post_save_handler(sender, instance, created):
if hasattr(instance, 'tags_for_instance'):
Tag.objects.update_tags(instance, instance.tags_for_instance)

Is there a way to express Django admin settings inside the models, instead of admin.py?

Talking about Django 1.1.1. I thought at one time (0.96) the kinds of things put inside of the admin.py file were part of an inner class of the model.
There's a certain beauty in having all of this in one place. But I don't know if this change was out of necessity. Any compelling reasons one way or the other?
They took away that particular magic, but there is nothing to keep you from putting your admin.ModelAdmin subclass right after the models.Model subclass itself. I prefer keeping them together myself because it's less likely I'll forget to add a field to the list to show in the admin display.
Perhaps it would not be a good idea to have this stuff in the models anyway, since it would be excess information when using the site as a non-admin? (For performance reasons)
There isn't any way to express the admin options inside the model definition as an inner class in the latest version. But there is no reason why you can't put your ModelAdmin class right after your Model class in your models.py file. You can then just call your admin.site.register() right after your definition.
You may run into a problem with the register() being called more than once for a model, which will generate an error. models.py should only get loaded once though so this should work. If not, you can definitely declare your ModelAdmin class in models.py, and then put all your register() calls in admin.py.
A couple reasons that I can think of to put it them in admin.py are:
Convention -- seems like this is becoming a best practice.
Decoupling -- the admin definitions don't really have much to do with the model.
Cleanness -- probably no need to fill up your models.py file with stuff you aren't going to change much.
But if your models.py file isn't going to be very long I can see the attraction of having them right next to each other.
use class Admin inside your models as u use class Meta
and then define what u want