I have a Model class that is structured as follows:
class Item(models.Model):
place = models.CharField(max_length=40)
item_id = models.IntegerField()
# etc.
#classmethod
def from_obj(cls, **kwargs):
i = Item()
# populate this from json data
# which needs a lot of translations applied
# before saving the data
So, from the above I have a way to create an Item instance via the from_obj classmethod. Now I want to process a json array that contains about 100 objects to create those 100 Item instances. Where should this be put? The function/method would look something like this:
def save_all():
objects = requests.get(...).json()
for obj in objects:
item = Item.from_obj(obj)
item.save()
Should this be a staticmethod within Item. A plain function outside it? Or should another class be created to 'manage' the Item. What is usually considered the best practice for something like this pattern?
If it was pure Python, I'd say either use a plain function or make it a classmethod.
Now this is django's ORM, and the convention for "table-level" operations here is to put them in a custom ModelManager class.
EDIT Except that in your example, there's an outgoing HTTP request, which suggests it might be better as a plain function - it's closer to a view or management command than to a ModelManager method
Why does python encourage one way and django encourages another way?
Python itself doesn't "encourage" anything special, those (plain function or classmethod) are just the obvious choices - it doesn't make sense to make this an instancemethod (you don't need any instance here) nor a staticmethod (since you do use the class).
Django's ORM is, well, an ORM - a framework that tries to provide a Python representation of the (rather formal and specialized) domain of relational databases. Separating row-level operations (model classes methods) from table-level operations (modelmanager classes) makes sense in this context.
The bulk_create method is ideal for this, you can add an additional classmethod that calls the API and bulk creates all the objects
#classmethod
def bulk_create_from_api(cls):
objects = requests.get(...).json()
cls.objects.bulk_create([cls.from_obj(obj) for obj in objects])
Used like this
Item.bulk_create_from_api()
Please note: bulk_create does not send any signals like pre_save or post_save
Related
I'm building a webapp that has optional Facebook Login. The users created through the Facebook API are handled differently at several points in my application. I want to encapsulate these differences in a subclass of Person that overrides methods.
class Person(Model):
def get_profile_picture(self):
return profile_pictures.url(self.picture)
class FacebookPerson(Person):
def get_profile_picture(self):
return 'http:/.../%s.jpg' % self.graph_id
I would like to avoid the nasty if self.graph_id and just query the Person model and get the right object for each user.
I've thought of hacking the metaclass to add the FacebookPerson as a base. Obviously I would like to avoid such voodoo.
I'm using Flask and Flask-SQLAlchemy.
The general idea would be to store the model's class name as metadata in each row, and when you instantiate the object, do something like:
def query(self):
# stuff
return model_class(data)
To do this in SQLAlchemy, you might look at making Person the base class to something like BasicPerson and FacebookPerson, and in Person.init(), use the metadata to initialize to the proper subclass.
For example, the idea would be than when this query returns, user will have been initialized to the proper subclass:
user = session.query(Person).filter_by(name='james').first()
You will probably need to modify this concept a bit for SQLAlchemy (I haven't used it in a while), but that's the general idea.
Or, you could do something like store the metadata in a cookie with the user_id, and then when they log in again, use the metadata to pass the proper class to the user query:
user = session.query(FacebookPerson).filter_by(name='james').first()
If you want this to be generic so that the metatdata is meaningful to non-Python clients, instead of storing the model's class name, store the model's "object_type" and have something in each client library that maps object_types to classes.
Django's ForeignRelatedObjectsDescriptor.create_manager(...) function dynamically creates the RelatedManager classes and subsequently initializes an instance of the dynamically created class.
If I wanted to override the RelatedManager.add(...) method, how would I do it?
The RelatedManager classes are created in file: django/db/models/fields/related.py.
An example of how I'd like to use a custom RelatedManager is...
class Record(Model):
string = CharField()
class Managed(Model):
record = ForeignKey('Record')
boolean = BooleanField()
def view_function(...):
record = Record(string='Example')
record.save()
record.managed_set.add(Managed(boolean=True)) # How to override add()?
Any suggestions would be appreciated.
I'm not sure what you need the override for - the default queryset already does what you want.
But to answer the question, you can define a custom Manager on the model and set use_for_related_fields=True to ensure it gets used as the automatic manager. See the documentation on controlling automatic Manager types.
I think I am having the same problem.
I have a custom manager that overrides self._db and get_query_set() to route it to different databases.
I dynamically created a model class, and has its _default_manager set with my custom manager.
This works for the class itself, but not for related field (foreign or many2many), even though I did set sets use_for_related_fields = True.
For related field, appending db_manager(dbname) (for example, record.managed_set.db_manager(dbname)) can fix all() method, but not for add() method.
To understand what I mean, see this django ticket: http://code.djangoproject.com/ticket/13358
I think it works for all(), but not add().
RelatedManager.add() calls RelatedManager._add_items() which calls Manager.bulk_create().
So if you extend Manager.bulk_create(), you might be able to achieve what you are after.
Basically I want to do something similar to annotating a queryset but with a call on a function in the model attached to the response.
Currently I have something like:
objs = WebSvc.objects.all().order_by('content_type', 'id')
for o in objs:
o.state = o.cast().get_state()
where get_state() is a function in the model that calls an external web service. I don't want to go down the road of caching the values. I was just hoping for a more succinct way of doing this.
One way to do this, using python properties:
class WebSvc(models.Model):
...
def _get_state():
return self.cast().get_state()
state = property(_get_state)
Advantages: will only run when the property is needed.
Possible disadvantage: when you call the property multiple times, the web service will be called anew (can be required behaviour but I doubt it). You can cache using memoization.
Other way, just do it by overriding init:
class WebSvc(models.Model):
...
def __init__(*args, **kwargs):
super(WebSvc, self).__init__(*args,**kwargs)
self.state = self.caste().get_state()
Advantages: Will only be computed once per instance without need for memoization.
Possible disadvantage: will be calculated for each instantiated object.
In most typical django cases however, you will only run once over properties of an object and you will probably not instantiate object where you won't use the .state property on. So in these cases the approaches are more or less similar in 'performance'.
I guess it's more of a python question than a django one, but I couldn't replicate this behavior anywhere else, so I'll use exact code that doesn't work as expected.
I was working on some dynamic forms in django, when I found this factory function snippet:
def get_employee_form(employee):
"""Return the form for a specific Board."""
employee_fields = EmployeeFieldModel.objects.filter(employee = employee).order_by ('order')
class EmployeeForm(forms.Form):
def __init__(self, *args, **kwargs):
forms.Form.__init__(self, *args, **kwargs)
self.employee = employee
def save(self):
"Do the save"
for field in employee_fields:
setattr(EmployeeForm, field.name, copy(type_mapping[field.type]))
return type('EmployeeForm', (forms.Form, ), dict(EmployeeForm.__dict__))
[from :http://uswaretech.com/blog/2008/10/dynamic-forms-with-django/]
And there's one thing that I don't understand, why returning modified EmployeeForm doesn't do the trick?
I mean something like this:
def get_employee_form(employee):
#[...]same function body as before
for field in employee_fields:
setattr(EmployeeForm, field.name, copy(type_mapping[field.type]))
return EmployeeForm
When I tried returning modified class django ignored my additional fields, but returning type()'s result works perfectly.
Lennart's hypothesis is correct: a metaclass is indeed the culprit. No need to guess, just look at the sources: the metaclass is DeclarativeFieldsMetaclass currently at line 53 of that file, and adds attributes base_fields and possibly media based on what attributes the class has at creation time. At line 329 ff you see:
class Form(BaseForm):
"A collection of Fields, plus their associated data."
# This is a separate class from BaseForm in order to abstract the way
# self.fields is specified. This class (Form) is the one that does the
# fancy metaclass stuff purely for the semantic sugar -- it allows one
# to define a form using declarative syntax.
# BaseForm itself has no way of designating self.fields.
__metaclass__ = DeclarativeFieldsMetaclass
This implies there's some fragility in creating a new class with base type -- the supplied black magic might or might not carry through! A more solid approach is to use the type of EmployeeForm which will pick up any metaclass that may be involved -- i.e.:
return type(EmployeeForm)('EmployeeForm', (forms.Form, ), EmployeeForm.__dict__)
(no need to copy that __dict__, btw). The difference is subtle but important: rather than using directly type's 3-args form, we use the 1-arg form to pick up the type (i.e., the metaclass) of the form class, then call THAT metaclass in the 3-args form.
Blackly magicallish indeed, but then that's the downside of frameworks which do such use of "fancy metaclass stuff purely for the semantic sugar" &c: you're in clover as long as you want to do exactly what the framework supports, but to get out of that support even a little bit may require countervailing wizardry (which goes some way towards explaining why often I'd rather use a lightweight, transparent setup, such as werkzeug, rather than a framework that ladles magic upon me like Rails or Django do: my mastery of deep black magic does NOT mean I'm happy to have to USE it in plain production code... but, that's another discussion;-).
I just tried this with straight non-django classes and it worked. So it's not a Python issue, but a Django issue.
And in this case (although I'm not 100% sure), it's a question of what the Form class does during class creation. I think it has a meta class, and that this meta class will finalize the form initialization during class creation. That means that any fields you add after class creation will be ignored.
Therefore you need to create a new class, as is done with the type() statement, so that the class creation code of the meta class is involved, now with the new fields.
It's worth noting that this code snippet is a very poor means to the desired end, and involves a common misunderstanding about Django Form objects - that a Form object should map one-to-one with an HTML form. The correct way to do something like this (which doesn't require messing with any metaclass magic) is to use multiple Form objects and an inline formset.
Or, if for some odd reason you really want to keep things in a single Form object, just manipulate self.fields in the Form's __init__ method.
When I have a given django model class like this:
class BaseClass(models.Model):
some_field = models.CharField(max_length = 80)
...
and some subclasses of it, for example
class SomeClass(BaseClass):
other_field = models.CharField(max_length = 80)
Then I know I can get the derived object by calling
base = BaseClass.objects.get(pk=3)
my_obj= base.someclass
Now the problem arises that I have multiple subclasses, and all I have is an instance of the base class. How can I get to the subclassed object without knowing it's class in advance?
The idea is to load a corresponding view and let that do the stuff. My project features only have a limited set of default actions for these models, like view, edit, delete etc. What I don't want is to expose the type of the given object via URL, so the "normal way" is not available
There isn't a built-in way.
Perhaps the best thing to do is to define a derived_type field on your base class, which is set automatically on save of the derived class. You could then have a get_derived method on the base which checks the value of derived_type and returns the actual derived object.
How can I get to the subclassed object without knowing it's class in advance?
Why would this be useful? If you don't know what class you want, you also won't know which methods to call or which attributes can be inspected.
The idea is to load a corresponding view and let that do the stuff. My project features only have a limited set of default actions for these models, like view, edit, delete etc. What I don't want is to expose the type of the given object via URL, so the "normal way" is not available
If you know the set of model subclasses ahead of time, or are willing to register them with a central view list, you can do something like this:
VIEWS = [('subclass_a', a_views), ('subclass_b', b_views)]
def edit(request):
base = (get base somehow)
for attrname, views in VIEWS:
if getattr(base, attrname) is not None:
return views['edit']
Depending on how many different kinds of views you have, you might abstract the search out into a separate function, so the end view is something like:
def edit(request):
return generic_base_view(request, 'edit')
For people looking into this answer a bit later than the question was posted and the accepted answer was given, in the newer versions of django, this is possible more directly, see:
django documentation about multi table inheritance
look at the example of places and restaurants. The base object is a place, and the subclass is a restaurant. You can get the subclass by place.restaurant which gives an exception you can catch if the place is not a restaurant.
I add this because the answer accepted might be a bit outdated and led me to the wrong track.
If you use the InheritanceManager from django-model-utils then you can select the subclasses when you query without knowing what they are ahead of time.
https://django-model-utils.readthedocs.org/en/latest/managers.html#inheritancemanager