mirror of
https://github.com/jazzband/django-categories.git
synced 2026-03-16 22:30:24 +00:00
Dramatically refactored how migrations are performed to work with Django 1.7
This commit is contained in:
parent
28ef4d5565
commit
acff7f02a3
4 changed files with 189 additions and 23 deletions
37
categories/apps.py
Normal file
37
categories/apps.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CategoriesConfig(AppConfig):
|
||||
name = 'categories'
|
||||
verbose_name = "Categories"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CategoriesConfig, self).__init__(*args, **kwargs)
|
||||
from django.db.models.signals import class_prepared
|
||||
class_prepared.connect(handle_class_prepared)
|
||||
|
||||
def ready(self):
|
||||
from django.db.models.signals import post_migrate
|
||||
from .migration import migrate_app
|
||||
|
||||
post_migrate.connect(migrate_app)
|
||||
|
||||
|
||||
def handle_class_prepared(sender, **kwargs):
|
||||
"""
|
||||
See if this class needs registering of fields
|
||||
"""
|
||||
from .settings import M2M_REGISTRY, FK_REGISTRY
|
||||
from .registration import registry
|
||||
sender_app = sender._meta.app_label
|
||||
sender_name = sender._meta.model_name
|
||||
|
||||
for key, val in FK_REGISTRY.items():
|
||||
app_name, model_name = key.split('.')
|
||||
if app_name == sender_app and sender_name == model_name:
|
||||
registry.register_model(app_name, sender, 'ForeignKey', val)
|
||||
|
||||
for key, val in M2M_REGISTRY.items():
|
||||
app_name, model_name = key.split('.')
|
||||
if app_name == sender_app and sender_name == model_name:
|
||||
registry.register_model(app_name, sender, 'ManyToManyField', val)
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
from django.db.models import ForeignKey, ManyToManyField
|
||||
|
||||
from .models import Category
|
||||
|
||||
|
||||
class CategoryM2MField(ManyToManyField):
|
||||
def __init__(self, **kwargs):
|
||||
from .models import Category
|
||||
if 'to' in kwargs:
|
||||
kwargs.pop('to')
|
||||
super(CategoryM2MField, self).__init__(to=Category, **kwargs)
|
||||
|
|
@ -12,6 +11,7 @@ class CategoryM2MField(ManyToManyField):
|
|||
|
||||
class CategoryFKField(ForeignKey):
|
||||
def __init__(self, **kwargs):
|
||||
from .models import Category
|
||||
if 'to' in kwargs:
|
||||
kwargs.pop('to')
|
||||
super(CategoryFKField, self).__init__(to=Category, **kwargs)
|
||||
|
|
|
|||
54
categories/migration.py
Normal file
54
categories/migration.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, connection
|
||||
from django.apps import apps
|
||||
|
||||
|
||||
def table_exists(table_name):
|
||||
"""
|
||||
Check if a table exists in the database
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def field_exists(app_name, model_name, field_name):
|
||||
"""
|
||||
Does the FK or M2M table exist in the database already?
|
||||
"""
|
||||
model = apps.get_model(app_name, model_name)
|
||||
table_name = model._meta.db_table
|
||||
cursor = connection.cursor()
|
||||
field_info = connection.introspection.get_table_description(cursor, table_name)
|
||||
field_names = [f.name for f in field_info]
|
||||
return field_name in field_names
|
||||
|
||||
|
||||
def drop_field(app_name, model_name, field_name):
|
||||
"""
|
||||
Drop the given field from the app's model
|
||||
"""
|
||||
app_config = apps.get_app_config(app_name)
|
||||
model = app_config.get_model(model_name)
|
||||
field = model._meta.get_field(field_name)
|
||||
with connection.schema_editor() as schema_editor:
|
||||
schema_editor.remove_field(model, field)
|
||||
|
||||
|
||||
def migrate_app(sender, app_config, verbosity=False, *args, **kwargs):
|
||||
"""
|
||||
Migrate all models of this app registered
|
||||
"""
|
||||
from .registration import registry
|
||||
|
||||
app_name = app_config.label
|
||||
|
||||
fields = [fld for fld in registry._field_registry.keys() if fld.startswith(app_name)]
|
||||
|
||||
with connection.schema_editor() as schema_editor:
|
||||
for fld in fields:
|
||||
model_name, field_name = fld.split('.')[1:]
|
||||
if field_exists(app_name, model_name, field_name):
|
||||
continue
|
||||
model = app_config.get_model(model_name)
|
||||
schema_editor.add_field(model, registry._field_registry[fld])
|
||||
|
|
@ -1,36 +1,111 @@
|
|||
"""
|
||||
These functions handle the adding of fields to other models
|
||||
"""
|
||||
from django.db.models import FieldDoesNotExist
|
||||
from django.db.models import FieldDoesNotExist, ForeignKey, ManyToManyField
|
||||
import fields
|
||||
from settings import FIELD_REGISTRY, MODEL_REGISTRY
|
||||
# from settings import self._field_registry, self._model_registry
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
|
||||
def register_m2m(model, field_name='categories', extra_params={}):
|
||||
return _register(model, field_name, extra_params, fields.CategoryM2MField)
|
||||
FIELD_TYPES = {
|
||||
'ForeignKey': ForeignKey,
|
||||
'ManyToManyField': ManyToManyField,
|
||||
}
|
||||
|
||||
|
||||
def register_fk(model, field_name='category', extra_params={}):
|
||||
return _register(model, field_name, extra_params, fields.CategoryFKField)
|
||||
class Registry(object):
|
||||
def __init__(self):
|
||||
self._field_registry = {}
|
||||
self._model_registry = {}
|
||||
|
||||
def register_model(self, app, model_name, field_type, field_definitions):
|
||||
"""
|
||||
Process for Django 1.7 +
|
||||
app: app name/label
|
||||
model_name: name of the model
|
||||
field_definitions: a string, tuple or list of field configurations
|
||||
field_type: either 'ForeignKey' or 'ManyToManyField'
|
||||
"""
|
||||
from django.apps import apps
|
||||
import collections
|
||||
|
||||
def _register(model, field_name, extra_params={}, field=fields.CategoryFKField):
|
||||
app_label = model._meta.app_label
|
||||
registry_name = ".".join((app_label, model.__name__, field_name)).lower()
|
||||
app_config = apps.get_app_config(app)
|
||||
app_label = app_config.label
|
||||
|
||||
if registry_name in FIELD_REGISTRY:
|
||||
return # raise AlreadyRegistered
|
||||
opts = model._meta
|
||||
try:
|
||||
opts.get_field(field_name)
|
||||
except FieldDoesNotExist:
|
||||
if app_label not in MODEL_REGISTRY:
|
||||
MODEL_REGISTRY[app_label] = []
|
||||
if model not in MODEL_REGISTRY[app_label]:
|
||||
MODEL_REGISTRY[app_label].append(model)
|
||||
FIELD_REGISTRY[registry_name] = field(**extra_params)
|
||||
FIELD_REGISTRY[registry_name].contribute_to_class(model, field_name)
|
||||
if isinstance(field_definitions, basestring):
|
||||
field_definitions = [field_definitions]
|
||||
elif not isinstance(field_definitions, collections.Iterable):
|
||||
raise ImproperlyConfigured(_('Field configuration for %(app)s should '
|
||||
'be a string or iterable') % {'app': app_config.label})
|
||||
|
||||
if field_type not in ('ForeignKey', 'ManyToManyField'):
|
||||
raise ImproperlyConfigured(_('`field_type` must be either `"ForeignKey"` or `"ManyToManyField"`.'))
|
||||
|
||||
try:
|
||||
if not hasattr(model_name, "_meta"):
|
||||
model = app_config.get_model(model_name)
|
||||
else:
|
||||
model = model_name
|
||||
model_name = model._meta.model_name
|
||||
opts = model._meta
|
||||
if app_label not in self._model_registry:
|
||||
self._model_registry[app_label] = []
|
||||
if model not in self._model_registry[app_label]:
|
||||
self._model_registry[app_label].append(model)
|
||||
except LookupError:
|
||||
raise ImproperlyConfigured('Model "%(model)s" doesn\'t exist in app "%(app)s".' % {'model': model_name, 'app': app})
|
||||
|
||||
if not isinstance(field_definitions, (tuple, list)):
|
||||
field_definitions = [field_definitions]
|
||||
|
||||
for fld in field_definitions:
|
||||
extra_params = {'to': 'categories.Category', 'null': True, 'blank': True}
|
||||
if isinstance(fld, basestring):
|
||||
field_name = fld
|
||||
elif isinstance(fld, dict):
|
||||
field_name = fld.pop('name')
|
||||
extra_params.update(fld)
|
||||
else:
|
||||
raise ImproperlyConfigured(_("%(settings)s doesn't recognize the "
|
||||
"value of %(app)s.%(model)s") % {
|
||||
'settings': 'CATEGORY_SETTINGS',
|
||||
'app': app,
|
||||
'model': model_name})
|
||||
registry_name = ".".join([app_config.label, model_name.lower(), field_name])
|
||||
if registry_name in self._field_registry:
|
||||
continue
|
||||
|
||||
try:
|
||||
opts.get_field(field_name)
|
||||
except FieldDoesNotExist:
|
||||
self._field_registry[registry_name] = FIELD_TYPES[field_type](**extra_params)
|
||||
self._field_registry[registry_name].contribute_to_class(model, field_name)
|
||||
|
||||
def register_m2m(self, model, field_name='categories', extra_params={}):
|
||||
return self._register(model, field_name, extra_params, fields.CategoryM2MField)
|
||||
|
||||
def register_fk(self, model, field_name='category', extra_params={}):
|
||||
return self._register(model, field_name, extra_params, fields.CategoryFKField)
|
||||
|
||||
def _register(self, model, field_name, extra_params={}, field=fields.CategoryFKField):
|
||||
app_label = model._meta.app_label
|
||||
registry_name = ".".join((app_label, model.__name__, field_name)).lower()
|
||||
|
||||
if registry_name in self._field_registry:
|
||||
return # raise AlreadyRegistered
|
||||
opts = model._meta
|
||||
try:
|
||||
opts.get_field(field_name)
|
||||
except FieldDoesNotExist:
|
||||
if app_label not in self._model_registry:
|
||||
self._model_registry[app_label] = []
|
||||
if model not in self._model_registry[app_label]:
|
||||
self._model_registry[app_label].append(model)
|
||||
self._field_registry[registry_name] = field(**extra_params)
|
||||
self._field_registry[registry_name].contribute_to_class(model, field_name)
|
||||
|
||||
registry = Registry()
|
||||
|
||||
|
||||
def _process_registry(registry, call_func):
|
||||
|
|
|
|||
Loading…
Reference in a new issue