magic-removal: Various small code cleanups
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1726 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
10df9db788
commit
fc27500389
|
@ -4,13 +4,13 @@ register = Library()
|
|||
|
||||
def path_breadcrumbs(path, overrides="", front=0, back=0):
|
||||
overs = overrides.split('/')
|
||||
comps = [""] * int(front) + path.split('/')[:-1]
|
||||
comps = [""] * int(front) + path.split('/')[:-1]
|
||||
backs = int(back) + len(comps)
|
||||
overs.extend( [None for x in range(len(overs) -1 ,len(comps)) ] )
|
||||
overs.extend([None for x in range(len(overs) -1, len(comps))])
|
||||
text = []
|
||||
for comp, ov in zip(comps,overs):
|
||||
for comp, ov in zip(comps, overs):
|
||||
label = ov or comp
|
||||
text.append("<a href='%s'>%s</a>›\n" % ( "../" * backs , label ) )
|
||||
text.append("<a href='%s'>%s</a>›\n" % ("../" * backs, label))
|
||||
backs -= 1
|
||||
return "".join(text)
|
||||
path_breadcrumbs = register.simple_tag(path_breadcrumbs)
|
|
@ -4,28 +4,25 @@ from django.contrib.admin.views.decorators import staff_member_required
|
|||
from django.contrib.admin.filterspecs import FilterSpec
|
||||
from django.core import formfields, template
|
||||
from django.core.template import loader
|
||||
from django.db import models
|
||||
from django.db.models.fields import BoundField, BoundFieldLine, BoundFieldSet
|
||||
from django.db.models.query import handle_legacy_orderlist
|
||||
from django.core.exceptions import Http404, ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
|
||||
from django.core.extensions import DjangoContext as Context
|
||||
from django.core.extensions import get_object_or_404, render_to_response
|
||||
from django.core.paginator import ObjectPaginator, InvalidPage
|
||||
from django.conf.settings import ADMIN_MEDIA_PREFIX
|
||||
try:
|
||||
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
|
||||
except ImportError:
|
||||
raise ImproperlyConfigured, "You don't have 'django.contrib.admin' in INSTALLED_APPS."
|
||||
from django.db import models
|
||||
from django.utils.html import strip_tags
|
||||
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
|
||||
from django.utils.text import capfirst, get_text_list
|
||||
from django.utils import dateformat
|
||||
from django.utils.dates import MONTHS
|
||||
from django.utils.html import escape
|
||||
from django.utils.html import escape, strip_tags
|
||||
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
|
||||
from django.utils.text import capfirst, get_text_list
|
||||
import operator
|
||||
from itertools import izip
|
||||
|
||||
from django.db.models.query import handle_legacy_orderlist
|
||||
|
||||
# The system will display a "Show all" link only if the total result count
|
||||
# is less than or equal to this setting.
|
||||
MAX_SHOW_ALL_ALLOWED = 200
|
||||
|
@ -59,7 +56,7 @@ def matches_app(mod, comps):
|
|||
modcomps = mod.__name__.split('.')[:-1] #HACK: leave off 'models'
|
||||
for c, mc in izip(comps, modcomps):
|
||||
if c != mc:
|
||||
return ([],False)
|
||||
return ([], False)
|
||||
return (comps[len(modcomps):], True)
|
||||
|
||||
def find_model(mod, remaining):
|
||||
|
@ -312,7 +309,7 @@ def change_list(request, path):
|
|||
c = Context(request, {
|
||||
'title': cl.title,
|
||||
'is_popup': cl.is_popup,
|
||||
'cl' : cl,
|
||||
'cl': cl,
|
||||
'path': path[:path.rindex('/')]
|
||||
})
|
||||
c.update({'has_add_permission': c['perms'][cl.app_label][cl.opts.get_add_permission()]}),
|
||||
|
@ -323,7 +320,7 @@ change_list = staff_member_required(change_list)
|
|||
|
||||
use_raw_id_admin = lambda field: isinstance(field.rel, (models.ManyToOne, models.ManyToMany)) and field.rel.raw_id_admin
|
||||
|
||||
def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_sets):
|
||||
def get_javascript_imports(opts, auto_populated_fields, ordered_objects, field_sets):
|
||||
# Put in any necessary JavaScript imports.
|
||||
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
|
||||
if auto_populated_fields:
|
||||
|
@ -476,7 +473,7 @@ def render_change_form(model, manipulator, app_label, context, add=False, change
|
|||
"admin/%s/change_form" % app_label ,
|
||||
"admin/change_form"], context_instance=context)
|
||||
|
||||
def log_add_message(user, opts,manipulator,new_object):
|
||||
def log_add_message(user, opts, manipulator, new_object):
|
||||
pk_value = getattr(new_object, opts.pk.attname)
|
||||
LogEntry.objects.log_action(user.id, opts.get_content_type_id(), pk_value, str(new_object), ADDITION)
|
||||
|
||||
|
@ -496,9 +493,9 @@ def add_stage(request, path, show_delete=False, form_url='', post_url='../change
|
|||
|
||||
if not errors and not request.POST.has_key("_preview"):
|
||||
new_object = manipulator.save(new_data)
|
||||
log_add_message(request.user, opts,manipulator,new_object)
|
||||
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name':opts.verbose_name, 'obj':new_object}
|
||||
pk_value = getattr(new_object,opts.pk.attname)
|
||||
log_add_message(request.user, opts, manipulator, new_object)
|
||||
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
|
||||
pk_value = getattr(new_object, opts.pk.attname)
|
||||
# Here, we distinguish between different save types by checking for
|
||||
# the presence of keys in request.POST.
|
||||
if request.POST.has_key("_continue"):
|
||||
|
@ -531,7 +528,7 @@ def add_stage(request, path, show_delete=False, form_url='', post_url='../change
|
|||
'form': form,
|
||||
'is_popup': request.REQUEST.has_key('_popup'),
|
||||
'show_delete': show_delete,
|
||||
'path' : path ,
|
||||
'path': path ,
|
||||
})
|
||||
|
||||
if object_id_override is not None:
|
||||
|
@ -540,7 +537,7 @@ def add_stage(request, path, show_delete=False, form_url='', post_url='../change
|
|||
return render_change_form(model, manipulator, app_label, c, add=True)
|
||||
add_stage = staff_member_required(add_stage)
|
||||
|
||||
def log_change_message(user, opts,manipulator,new_object):
|
||||
def log_change_message(user, opts, manipulator, new_object):
|
||||
pk_value = getattr(new_object, opts.pk.column)
|
||||
# Construct the change message.
|
||||
change_message = []
|
||||
|
@ -556,7 +553,6 @@ def log_change_message(user, opts,manipulator,new_object):
|
|||
LogEntry.objects.log_action(user.id, opts.get_content_type_id(), pk_value, str(new_object), CHANGE, change_message)
|
||||
|
||||
def change_stage(request, path, object_id):
|
||||
|
||||
model, app_label = get_model_and_app(path)
|
||||
opts = model._meta
|
||||
#mod, opts = _get_mod_opts(app_label, module_name)
|
||||
|
@ -580,9 +576,9 @@ def change_stage(request, path, object_id):
|
|||
manipulator.do_html2python(new_data)
|
||||
if not errors and not request.POST.has_key("_preview"):
|
||||
new_object = manipulator.save(new_data)
|
||||
log_change_message(request.user,opts,manipulator,new_object)
|
||||
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj':new_object}
|
||||
pk_value = getattr(new_object,opts.pk.attname)
|
||||
log_change_message(request.user, opts, manipulator, new_object)
|
||||
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object}
|
||||
pk_value = getattr(new_object, opts.pk.attname)
|
||||
if request.POST.has_key("_continue"):
|
||||
request.user.add_message(msg + ' ' + _("You may edit it again below."))
|
||||
if request.REQUEST.has_key('_popup'):
|
||||
|
@ -632,11 +628,10 @@ def change_stage(request, path, object_id):
|
|||
'form': form,
|
||||
'object_id': object_id,
|
||||
'original': manipulator.original_object,
|
||||
'is_popup' : request.REQUEST.has_key('_popup'),
|
||||
'path' : path ,
|
||||
'is_popup': request.REQUEST.has_key('_popup'),
|
||||
'path': path ,
|
||||
})
|
||||
|
||||
return render_change_form(model,manipulator, app_label, c, change=True)
|
||||
return render_change_form(model, manipulator, app_label, c, change=True)
|
||||
|
||||
def _nest_help(obj, depth, val):
|
||||
current = obj
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import django.db.models.manipulators
|
||||
import django.db.models.manager
|
||||
|
||||
from django.db.models.fields import AutoField
|
||||
from django.db.models.fields import AutoField
|
||||
from django.db.models.fields.related import OneToOne, ManyToOne
|
||||
from django.db.models.related import RelatedObject
|
||||
from django.db.models.query import orderlist2sql
|
||||
from django.db.models.options import Options
|
||||
from django.db import connection, backend
|
||||
from django.db.models import signals
|
||||
|
||||
from django.dispatch import dispatcher
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.functional import curry
|
||||
|
||||
import re
|
||||
import types
|
||||
import sys
|
||||
|
@ -27,7 +24,6 @@ get_module_name = lambda class_name: class_name.lower() + 's'
|
|||
# Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
|
||||
get_verbose_name = lambda class_name: re.sub('([A-Z])', ' \\1', class_name).lower().strip()
|
||||
|
||||
|
||||
class ModelBase(type):
|
||||
"Metaclass for all models"
|
||||
def __new__(cls, name, bases, attrs):
|
||||
|
@ -42,13 +38,13 @@ class ModelBase(type):
|
|||
except KeyError:
|
||||
meta_attrs = {}
|
||||
|
||||
# Create the class, we need to add the options to it.
|
||||
new_class = type.__new__(cls, name, bases, { '__module__' : attrs.pop('__module__') })
|
||||
# Create the class.
|
||||
new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')})
|
||||
|
||||
opts = Options(
|
||||
module_name = meta_attrs.pop('module_name', get_module_name(name)),
|
||||
# If the verbose_name wasn't given, use the class name,
|
||||
# converted from InitialCaps to "lowercase with spaces".
|
||||
# converted from "InitialCaps" to "lowercase with spaces".
|
||||
verbose_name = meta_attrs.pop('verbose_name', get_verbose_name(name)),
|
||||
verbose_name_plural = meta_attrs.pop('verbose_name_plural', ''),
|
||||
db_table = meta_attrs.pop('db_table', ''),
|
||||
|
@ -81,12 +77,10 @@ class ModelBase(type):
|
|||
# Cache the app label.
|
||||
opts.app_label = app_label
|
||||
|
||||
#Add all attributes to the class
|
||||
# Add all attributes to the class.
|
||||
for obj_name, obj in attrs.items():
|
||||
new_class.add_to_class(obj_name, obj)
|
||||
|
||||
|
||||
|
||||
# Give the class a docstring -- its definition.
|
||||
if new_class.__doc__ is None:
|
||||
new_class.__doc__ = "%s.%s(%s)" % (opts.module_name, name, ", ".join([f.name for f in opts.fields]))
|
||||
|
@ -97,12 +91,9 @@ class ModelBase(type):
|
|||
opts._prepare()
|
||||
new_class._prepare()
|
||||
|
||||
|
||||
|
||||
# Populate the _MODELS member on the module the class is in.
|
||||
app_package.__dict__.setdefault('_MODELS', []).append(new_class)
|
||||
|
||||
|
||||
return new_class
|
||||
|
||||
def cmp_cls(x, y):
|
||||
|
@ -117,13 +108,6 @@ def cmp_cls(x, y):
|
|||
class Model(object):
|
||||
__metaclass__ = ModelBase
|
||||
|
||||
def add_to_class(cls, name, attribute):
|
||||
if hasattr(attribute, 'contribute_to_class'):
|
||||
attribute.contribute_to_class(cls, name)
|
||||
else:
|
||||
setattr(cls, name, attribute)
|
||||
add_to_class = classmethod(add_to_class)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s object>' % self.__class__.__name__
|
||||
|
||||
|
@ -134,7 +118,7 @@ class Model(object):
|
|||
return not self.__eq__(other)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
dispatcher.send( signal = signals.pre_init, sender = self.__class__, args=args, kwargs=kwargs)
|
||||
dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
|
||||
if kwargs:
|
||||
for f in self._meta.fields:
|
||||
if isinstance(f.rel, ManyToOne):
|
||||
|
@ -165,7 +149,14 @@ class Model(object):
|
|||
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
|
||||
for i, arg in enumerate(args):
|
||||
setattr(self, self._meta.fields[i].attname, arg)
|
||||
dispatcher.send( signal = signals.post_init, sender = self.__class__, instance=self)
|
||||
dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
|
||||
|
||||
def add_to_class(cls, name, attribute):
|
||||
if hasattr(attribute, 'contribute_to_class'):
|
||||
attribute.contribute_to_class(cls, name)
|
||||
else:
|
||||
setattr(cls, name, attribute)
|
||||
add_to_class = classmethod(add_to_class)
|
||||
|
||||
def _prepare(cls):
|
||||
# Creates some methods once self._meta has been populated.
|
||||
|
@ -173,7 +164,7 @@ class Model(object):
|
|||
cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True)
|
||||
cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False)
|
||||
|
||||
dispatcher.send( signal = signals.class_prepared, sender = cls)
|
||||
dispatcher.send(signal=signals.class_prepared, sender=cls)
|
||||
#RelatedField.do_pending_lookups(cls)
|
||||
|
||||
_prepare = classmethod(_prepare)
|
||||
|
@ -182,7 +173,7 @@ class Model(object):
|
|||
# Run any pre-save hooks.
|
||||
if hasattr(self, '_pre_save'):
|
||||
self._pre_save()
|
||||
dispatcher.send( signal=signals.pre_save, sender = self.__class__, instance = self )
|
||||
dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
|
||||
|
||||
non_pks = [f for f in self._meta.fields if not f.primary_key]
|
||||
cursor = connection.cursor()
|
||||
|
@ -261,8 +252,7 @@ class Model(object):
|
|||
|
||||
def delete(self, ignore_objects=None):
|
||||
assert getattr(self, self._meta.pk.attname) is not None, "%r can't be deleted because it doesn't have an ID."
|
||||
ignore_objects = \
|
||||
ignore_objects and dict([ (o.__class,o.__get_pk_val) for o in ignore_objects ]) or {}
|
||||
ignore_objects = ignore_objects and dict([(o.__class,o.__get_pk_val) for o in ignore_objects]) or {}
|
||||
|
||||
seen_objs = {}
|
||||
self.__collect_sub_objects(seen_objs, ignore_objects)
|
||||
|
@ -282,7 +272,7 @@ class Model(object):
|
|||
if hasattr(instance, '_pre_delete'):
|
||||
instance._pre_delete()
|
||||
|
||||
dispatcher.send(signal=signals.pre_delete, sender = cls, instance = instance )
|
||||
dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance)
|
||||
|
||||
for related in cls._meta.get_all_related_many_to_many_objects():
|
||||
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
|
||||
|
@ -294,14 +284,11 @@ class Model(object):
|
|||
(backend.quote_name(f.get_m2m_db_table(cls._meta)),
|
||||
backend.quote_name(cls._meta.object_name.lower() + '_id')),
|
||||
[pk_val])
|
||||
|
||||
for field in cls._meta.fields:
|
||||
if field.rel and field.null and field.rel.to in seen_cls:
|
||||
cursor.execute("UPDATE %s SET %s = NULL WHERE %s =%%s" % \
|
||||
( backend.quote_name(cls._meta.db_table),
|
||||
backend.quote_name(field.column),
|
||||
backend.quote_name(cls._meta.pk.column)),
|
||||
[pk_val] )
|
||||
cursor.execute("UPDATE %s SET %s = NULL WHERE %s=%%s" % \
|
||||
(backend.quote_name(cls._meta.db_table), backend.quote_name(field.column),
|
||||
backend.quote_name(cls._meta.pk.column)), [pk_val])
|
||||
|
||||
seen_tups.reverse()
|
||||
|
||||
|
@ -312,7 +299,7 @@ class Model(object):
|
|||
|
||||
setattr(self, cls._meta.pk.attname, None)
|
||||
|
||||
dispatcher.send(signal=signals.post_delete, sender = cls, instance = instance )
|
||||
dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance)
|
||||
|
||||
if hasattr(instance, '_post_delete'):
|
||||
instance._post_delete()
|
||||
|
@ -321,7 +308,6 @@ class Model(object):
|
|||
|
||||
delete.alters_data = True
|
||||
|
||||
|
||||
def _get_FIELD_display(self, field):
|
||||
value = getattr(self, field.attname)
|
||||
return dict(field.choices).get(value, value)
|
||||
|
@ -504,7 +490,6 @@ class Model(object):
|
|||
|
||||
_add_related.alters_data = True
|
||||
|
||||
|
||||
# Handles related many-to-many object retrieval.
|
||||
# Examples: Album.get_song(), Album.get_song_list(), Album.get_song_count()
|
||||
def _get_related_many_to_many(self, method_name, rel_class, rel_field, **kwargs):
|
||||
|
@ -529,10 +514,6 @@ class Model(object):
|
|||
cursor.executemany(sql, [(this_id, i) for i in id_list])
|
||||
connection.commit()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
############################################
|
||||
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
||||
############################################
|
||||
|
@ -568,5 +549,3 @@ def method_get_order(ordered_obj, self):
|
|||
|
||||
def get_absolute_url(opts, func, self):
|
||||
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self)
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ from django.utils.text import capfirst
|
|||
from django.utils.translation import gettext_lazy, ngettext
|
||||
import datetime, os
|
||||
|
||||
|
||||
# Random entropy string used by "default" param.
|
||||
NOT_PROVIDED = 'oijpwojefiojpanv'
|
||||
|
||||
|
@ -35,9 +34,7 @@ def manipulator_valid_rel_key(f, self, field_data, all_data):
|
|||
|
||||
def manipulator_validator_unique(f, opts, self, field_data, all_data):
|
||||
"Validates that the value is unique for this field."
|
||||
|
||||
lookup_type = f.get_validator_unique_lookup_type()
|
||||
|
||||
try:
|
||||
old_obj = self.__class__._default_manager.get_object(**{lookup_type: field_data})
|
||||
except ObjectDoesNotExist:
|
||||
|
@ -97,7 +94,6 @@ class Field(object):
|
|||
help_text='', db_column=None):
|
||||
self.name = name
|
||||
self.verbose_name = verbose_name
|
||||
|
||||
self.primary_key = primary_key
|
||||
self.maxlength, self.unique = maxlength, unique
|
||||
self.blank, self.null = blank, null
|
||||
|
@ -118,9 +114,8 @@ class Field(object):
|
|||
self.creation_counter = Field.creation_counter
|
||||
Field.creation_counter += 1
|
||||
|
||||
|
||||
def __cmp__(self,other ):
|
||||
#This is because bisect does not take a comparison function. grrr.
|
||||
# This is needed because bisect does not take a comparison function.
|
||||
return cmp(self.creation_counter, other.creation_counter)
|
||||
|
||||
def set_attributes_from_name(self, name):
|
||||
|
@ -292,7 +287,6 @@ class Field(object):
|
|||
first_choice = include_blank and blank_choice or []
|
||||
if self.choices:
|
||||
return first_choice + list(self.choices)
|
||||
|
||||
rel_model = self.rel.to
|
||||
return first_choice + [(getattr(x, rel_model._meta.pk.attname), str(x))
|
||||
for x in rel_model._default_manager.get_list(**rel_model._meta.limit_choices_to)]
|
||||
|
@ -365,7 +359,7 @@ class DateField(Field):
|
|||
empty_strings_allowed = False
|
||||
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
|
||||
self.auto_now, self.auto_now_add = auto_now, auto_now_add
|
||||
#HACKs : auto_now_add/auto_now should be done as a default or a pre_save...
|
||||
#HACKs : auto_now_add/auto_now should be done as a default or a pre_save.
|
||||
if auto_now or auto_now_add:
|
||||
kwargs['editable'] = False
|
||||
kwargs['blank'] = True
|
||||
|
|
|
@ -15,7 +15,6 @@ class Options:
|
|||
order_with_respect_to=None, module_constants=None):
|
||||
# Move many-to-many related fields from self.fields into self.many_to_many.
|
||||
self.fields, self.many_to_many = [], []
|
||||
|
||||
self.module_name, self.verbose_name = module_name, verbose_name
|
||||
self.verbose_name_plural = verbose_name_plural or verbose_name + 's'
|
||||
self.db_table = db_table
|
||||
|
@ -27,7 +26,6 @@ class Options:
|
|||
self.object_name, self.app_label = object_name, app_label
|
||||
self.get_latest_by = get_latest_by
|
||||
self.order_with_respect_to = order_with_respect_to
|
||||
|
||||
self.module_constants = module_constants or {}
|
||||
self.admin = admin
|
||||
|
||||
|
@ -77,14 +75,12 @@ class Options:
|
|||
self.db_table = "%s_%s" % (self.app_label, self.module_name)
|
||||
|
||||
def add_field(self, field):
|
||||
# Insert the fields in the order that they were created. The
|
||||
# "creation_counter" is needed because metaclasses don't preserve the
|
||||
# attribute order.
|
||||
# Insert the given field in the order in which it was created, using
|
||||
# the "creation_counter" attribute of the field.
|
||||
if field.rel and isinstance(field.rel, ManyToMany):
|
||||
self.many_to_many.insert(bisect(self.many_to_many, field), field)
|
||||
else:
|
||||
self.fields.insert(bisect(self.fields,field),field)
|
||||
|
||||
self.fields.insert(bisect(self.fields, field), field)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Options for %s>' % self.module_name
|
||||
|
@ -95,11 +91,10 @@ class Options:
|
|||
def get_content_type_id(self):
|
||||
"Returns the content-type ID for this object type."
|
||||
if not hasattr(self, '_content_type_id'):
|
||||
import django.models.core
|
||||
manager = django.models.core.ContentType.objects
|
||||
self._content_type_id = \
|
||||
manager.get_object(python_module_name__exact=self.module_name,
|
||||
package__label__exact=self.app_label).id
|
||||
from django.models.core import ContentType
|
||||
self._content_type_id = ContentType.objects.get_object(
|
||||
python_module_name__exact=self.module_name,
|
||||
package__label__exact=self.app_label).id
|
||||
return self._content_type_id
|
||||
|
||||
def get_field(self, name, many_to_many=True):
|
||||
|
|
Loading…
Reference in New Issue