Django - global setting to specify all models as non-managed - django

I'm writing a Django app, but have a separate process for creating / managing the tables. In other words, I don't want Django to manage any of the DB tables. To accomplish this, I use managed = False in the Meta class, like:
class School(models.Model):
schoolid = models.IntegerField(primary_key=True)
schooldisplayname = models.CharField(max_length=100)
address = models.CharField(max_length=100)
city = models.CharField(max_length=100)
department = models.CharField(max_length=100)
class Meta:
managed = False
But it's annoying to have to always specify this for each model. Is there a way to apply this as a global setting to all my models by default?

I'm not quite sure how to do exactly this. It might be better to mark them all as managed = false so it's explicit.
You can disable migrations globally in settings. Not quite the same...
from settings import *
class DisableMigrations(object):
def __contains__(self, item):
return True
def __getitem__(self, item):
return 'notmigrations'
MIGRATION_MODULES = DisableMigrations()

While I am not sure if a global way exists if you are looking for a quick way to mark managed=False for your existing models. You can do this.
python manage.py inspectdb > yourapp/models.py
You get managed = False in every model class by default.

Related

Django - Simplify Proxy Model to single class

I have User table in my DB, they can be active or inactive. If I only want to query on active user, I define a Proxy Model like following.
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
class UserActive(models.Manager):
def get_queryset(self):
return super(UserActive, self).get_queryset().filter(active=True)
class ActiveUser(User):
objects = UserActive()
class Meta:
proxy = True
Then by working with ActiveUser, I can do my calculation/statistic with only active user.
The problem is, I need to define both UserActive and ActiveUser class, it seems awkward to me. Because with each main class (in this case is User), we need to define two other classes. Imaging we have several other model need to implement Proxy, the code would look messy. May I know if we can have more elegant way ?
Thanks
Alex
I would really avoid overwriting the .objects manager, and use this as some sort of implicit filtering. The Zen of Python is explicit is better than implicit, by using ActiveUser, you basically implement a filtering manager, but propose it like the entire set.
Perhaps a more elegant solution is to define multiple managers. So we can construct a filtering manager decorator:
def filter_manager(**kwargs):
def decorator(klass):
def get_queryset(self):
return super(klass, self).get_queryset().filter(**kwargs)
klass.get_queryset = get_queryset
return klass
return decorator
This decorator will however throw away a get_queryset that is defined on the manager itself, so you can not perform an extra patch with this.
Now we can define some managers in a rather elegant way:
#filter_manager(active=True)
class ActiveManager(models.Manager):
pass
#filter_manager(active=False)
class InactiveManager(models.Manager):
pass
Finally we can add these managers to the User model, and use explicit names:
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
objects = models.Manager()
active_users = ActiveManager()
inactive_users = InactiveManager()
So now we can use User.active_users to query for the active users. We thus have no proxy models, and can query with User.active_users.count() for example (well we can perform all operations like with .objects but then for .active_users.
I created a new Django project, with only User model. My models.py look like this
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
def filter_manager(**kwargs):
def decorator(klass):
def get_queryset(self):
return super(klass, self).get_queryset().filter(**kwargs)
klass.get_queryset = get_queryset
return klass
return decorator
#filter_manager(active=True)
class ActiveManager(models.Manager):
pass
#filter_manager(active=False)
class InactiveManager(models.Manager):
pass
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
active_user = ActiveManager()
When I tried User.objects.all().
Error: type object 'User' has no attribute 'objects'

Django GenericForeignKey limit to ContentTypes that inherit from a particular abstract model

In my application's models I need a way of linking Problems and Solutions -- every Problem can have multiple Solutions and a given Solution can map back to multiple Problems.
Solution is an abstract base class, since there can be many varieties of Solutions. So, I figured out I need a mapping table ProblemSolutionMapping which uses a GenericForeignKey to accommodate all those child classes. But I'm trying to figure out how to limit the classes to just the children of Solutions and not all the classes available in the whole application, which is what is currently happening.
# Thanks to http://stackoverflow.com/a/23555691/1149759
class Solution(models.Model):
...
#classmethod
def get_subclasses(cls):
content_types = ContentType.objects.filter(app_label=cls._meta.app_label)
models = [ct.model_class() for ct in content_types]
return [model for model in models
if (model is not None and
issubclass(model, cls) and
model is not cls)]
class Meta:
abstract = True
class ProblemSolutionMapping(models.Model):
problem = models.ForeignKey(Problem)
content_type = models.ForeignKey(ContentType,
limit_choices_to=Solution.get_subclasses()) # <==== This is the issue
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
The issue is that when I start up my Django app, the call to ContentType.objects.filter(app_label=cls._meta.app_label) throws the error:
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
Not sure what to do -- I tried making the mapping table the last one in the relevant models file (all the child classes are defined above it in the same file), but it made no difference. Is this something that I have to move into the admin form? Or is there some other way to do this at the model level?
(Django 1.9, in case it matters.)
Thanks in advance for your help!
Refrecing to a model during import time is no longer supported from django 1.7. You should use your models after all application are loaded. So you should either Staticly pass a list to your limit_choices_to or Use Q object like this:
limit_choices_to=models.Q(app_label = 'app', model = 'a') | models.Q(app_label = 'app', model = 'b')
Also you can limit what shows to user in form level
So I arrived here looking for the answer. Based on Mehran's post, I developed the below approach which is similar to yours. Instead the limit_choice_to calls a method that returns a runtime created Q object.
Below is the part that is similar to your get_subclasses.
def get_subclasses(cls, *args, **kwargs):
for app_config in apps.get_app_configs():
for app_model in app_config.get_models():
model_classes = [c.__name__ for c in inspect.getmro(app_model)]
if cls.__name__ in model_classes:
yield app_model
This creates the Q filter(s) for us (in my implementation, this is just a plain old method not attached to any class, but I suppose it could be):
def get_content_choices():
query_filter = None
for cls in Solution.get_subclasses():
app_label, model = cls._meta.label_lower.split('.')
current_filter = models.Q(app_label=app_label, model=model)
if query_filter is None:
query_filter = current_filter
else:
query_filter |= current_filter
return query_filter
And finally, in our model:
class ProblemSolutionMapping(models.Model):
...
content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_choices())
...

Django table naming convention. Can I change its behavior?

When Django creates tables it give them names of form app_class. I'm in the position of retrofitting a different (but basically similar in content) database to a Django installation. My tables name are not prepended with app_.
I could recreate my database and its tables accordingly but I'd like to see if Django has the flexibility to modify how it handles table names.
That is, I have a table coi_fs - I'd like to see if I can change the Django installation such that it will refer not to app_coi_fs but simply coi_fs?
If you already have a database I would recommend using the database introspection option. This will create the models needed to use your current database as is.
$ django-admin.py inspectdb > models.py
To answer your original question though, from the docs (https://docs.djangoproject.com/en/dev/ref/models/options/#table-names), you can use the db_table meta property.
models.py
class DjangoSite(models.Model):
domain = models.CharField(max_length=100)
name = models.CharField(max_length=50)
class Meta:
db_table = u'site'
Of course you can do it in Django, just change Model's metaclass:
from django.db.models.base import ModelBase
class ModelMetaClass(ModelBase):
def __new__(cls, name, bases, attrs):
new_class = super().__new__(cls, name, bases, attrs)
new_class._meta.db_table = some_awesome_logic_implemented_by_you()
return new_class
and then in the Model:
class DjangoSite(models.Model, metaclass=ModelMetaClass):
# __metaclass__ = ModelMetaClass # Python 2.x
class Meta:
pass
and you are done!
If you want to universally rename all tables, edit django source. This seems dangerous but if you explicitly want to change the Django installation than have at it. https://github.com/django/django/blob/master/django/db/models/options.py
old options.py
self.db_table = "%s_%s" % (self.app_label, self.model_name)
new options.py
self.db_table = self.model_name

Django - "last_modified" or "auto_now_add" for an App (or more than one Model?)

I know Django has a feature of last_modified field (models.DateTimeField(auto_now_add=True)
)..
but let's say I have a certain App, and I want to know when was the last change for any of its Model (I don't really care which model was changed, I just want to know when was the latest change for this app..)
do I really have to write a last_modified field for each model (I have 9 of them for the moment...), and then check for each of them which is the latest?
any help will be appreciated :)
Thanks
You could create a base class that defines the last_modified field...
class YourBaseClassName(models.Model):
last_modified = models.DateTimeField(auto_now=True)
and then inherit from that
class AnotherClass(YourBaseClassName):
another_field = models.CharField(max_length=50)
In The End I made a table for constants for my app (actually I had it before for use of other things).
so the Table looks like this:
from django.db import models
from django.db.models.signals import post_save
class Constant(models.Model):
name = models.CharField(max_length=50)
value = models.CharField(max_length=50)
and added a consant named "version_date".
Than, I added this code to the bottom of my models.py, to track all changes in all the models in the app.
myapp = models.get_app('myapp')
models2track = models.get_models(myapp)
def update_version(sender, **kwargs):
for model in models2track:
post_save.disconnect(update_version, sender=model, dispatch_uid="some_uid"+model._meta.db_table)
version_date = Constant.objects.get_or_create(id=1,name="version date")[0]
version_date.value = str(int(time.time()))
version_date.save()
for model in models2track:
post_save.connect(update_version, sender=model, dispatch_uid="some_uid"+model._meta.db_table)
for model in models2track:
post_save.connect(update_version, sender=model, dispatch_uid="some_uid"+model._meta.db_table)
This way, I don't need to change my DB Schema.. only need to add the code mentioned.
thanks all

Unique model field in Django and case sensitivity (postgres)

Consider the following situation: -
Suppose my app allows users to create the states / provinces in their
country. Just for clarity, we are considering only ASCII characters
here.
In the US, a user could create the state called "Texas". If this app
is being used internally, let's say the user doesn't care if it is
spelled "texas" or "Texas" or "teXas"
But importantly, the system should prevent creation of "texas" if
"Texas" is already in the database.
If the model is like the following:
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
The uniqueness would be case-sensitive in postgres; that is, postgres
would allow the user to create both "texas" and "Texas" as they are
considered unique.
What can be done in this situation to prevent such behavior. How does
one go about providing case-insenstitive uniqueness with Django and
Postgres
Right now I'm doing the following to prevent creation of case-
insensitive duplicates.
class CreateStateForm(forms.ModelForm):
def clean_name(self):
name = self.cleaned_data['name']
try:
State.objects.get(name__iexact=name)
except ObjectDoesNotExist:
return name
raise forms.ValidationError('State already exists.')
class Meta:
model = State
There are a number of cases where I will have to do this check and I'm not keen on having to write similar iexact checks everywhere.
Just wondering if there is a built-in or
better way? Perhaps db_type would help? Maybe some other solution exists?
You could define a custom model field derived from models.CharField.
This field could check for duplicate values, ignoring the case.
Custom fields documentation is here http://docs.djangoproject.com/en/dev/howto/custom-model-fields/
Look at http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/files.py for an example of how to create a custom field by subclassing an existing field.
You could use the citext module of PostgreSQL https://www.postgresql.org/docs/current/static/citext.html
If you use this module, the the custom field could define "db_type" as CITEXT for PostgreSQL databases.
This would lead to case insensitive comparison for unique values in the custom field.
Alternatively you can change the default Query Set Manager to do case insensitive look-ups on the field. In trying to solve a similar problem I came across:
http://djangosnippets.org/snippets/305/
Code pasted here for convenience:
from django.db.models import Manager
from django.db.models.query import QuerySet
class CaseInsensitiveQuerySet(QuerySet):
def _filter_or_exclude(self, mapper, *args, **kwargs):
# 'name' is a field in your Model whose lookups you want case-insensitive by default
if 'name' in kwargs:
kwargs['name__iexact'] = kwargs['name']
del kwargs['name']
return super(CaseInsensitiveQuerySet, self)._filter_or_exclude(mapper, *args, **kwargs)
# custom manager that overrides the initial query set
class TagManager(Manager):
def get_query_set(self):
return CaseInsensitiveQuerySet(self.model)
# and the model itself
class Tag(models.Model):
name = models.CharField(maxlength=50, unique=True, db_index=True)
objects = TagManager()
def __str__(self):
return self.name
a very simple solution:
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def clean(self):
self.name = self.name.capitalize()
Explicit steps for Mayuresh's answer:
in postgres do: CREATE EXTENSION citext;
in your models.py add:
from django.db.models import fields
class CaseInsensitiveTextField(fields.TextField):
def db_type(self, connection):
return "citext"
reference: https://github.com/zacharyvoase/django-postgres/blob/master/django_postgres/citext.py
in your model use: name = CaseInsensitiveTextField(unique=True)
On the Postgres side of things, a functional unique index will let you enforce unique values without case. citext is also noted, but this will work with older versions of PostgreSQL and is a useful technique in general.
Example:
# create table foo(bar text);
CREATE TABLE
# create unique index foo_bar on foo(lower(bar));
CREATE INDEX
# insert into foo values ('Texas');
INSERT 0 1
# insert into foo values ('texas');
ERROR: duplicate key value violates unique constraint "foo_bar"
Besides already mentioned option to override save, you can simply store all text in lower case in database and capitalize them on displaying.
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def save(self, force_insert=False, force_update=False):
self.name = self.name.lower()
super(State, self).save(force_insert, force_update)
You can use lookup='iexact' in UniqueValidator on serializer, like this:
class StateSerializer(serializers.ModelSerializer):
name = serializers.CharField(validators=[
UniqueValidator(
queryset=models.State.objects.all(),lookup='iexact'
)]
django version: 1.11.6
If you don't want to use a postgres-specific solution, you can create a unique index on the field with upper() to enforce uniqueness at the database level, then create a custom Field mixin that overrides get_lookup() to convert case-sensitive lookups to their case-insensitive versions. The mixin looks like this:
class CaseInsensitiveFieldMixin:
"""
Field mixin that uses case-insensitive lookup alternatives if they exist.
"""
LOOKUP_CONVERSIONS = {
'exact': 'iexact',
'contains': 'icontains',
'startswith': 'istartswith',
'endswith': 'iendswith',
'regex': 'iregex',
}
def get_lookup(self, lookup_name):
converted = self.LOOKUP_CONVERSIONS.get(lookup_name, lookup_name)
return super().get_lookup(converted)
And you use it like this:
from django.db import models
class CICharField(CaseInsensitiveFieldMixin, models.CharField):
pass
class CIEmailField(CaseInsensitiveFieldMixin, models.EmailField):
pass
class TestModel(models.Model):
name = CICharField(unique=True, max_length=20)
email = CIEmailField(unique=True)
You can read more about this approach here.
You can do this by overwriting the Model's save method - see the docs. You'd basically do something like:
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def save(self, force_insert=False, force_update=False):
if State.objects.get(name__iexact = self.name):
return
else:
super(State, self).save(force_insert, force_update)
Also, I may be wrong about this, but the upcoming model-validation SoC branch will allow us to do this more easily.
Solution from suhail worked for me without the need to enable citext, pretty easy solution only a clean function and instead of capitalize I used upper(). Mayuresh's solution also works but changed the field from CharField to TextField.
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def clean(self):
self.name = self.name.upper()