The person on our team who initially taught us django (and has subsequently left) utilized a controllers.py file for helper functions. A lot of these functions are directly related to classes. I prefer to use #staticmethod to house these helpers with the classes they're related to. For example, rather than doing this:
# controllers.py
def process_entry():
# do some exciting stuff that might not have to do with an instance
Called like this:
from myapp.controllers import process_entry
process_entry()
I'd prefer this:
# models.py
class Entry(models.Model):
name = CharField...
# some other fields
#staticmethod
def process_entry():
# do some exciting stuff that might not have to do with an instance
Called like so:
from myapp.models import Entry
Entry.process_entry()
Is there a preferred, standard way to deal with situations like this or is it just personal preference? Does anyone else utilize a controllers.py file? What goes in there?
Thanks.
EDIT:
Possibly a better example, commissions.
# models.py
class Commission(models.Model):
check = ForeignKey
payment = ForeignKey
# other fields
class Check(models.Model):
# fields
class Payment(models.Model):
# fields
Any time a Check or Payment instance is modified an adjustment as to be made to any related Commission instances or any time someone wants to manually run commissions, they can do so. So where should run_commissions() go? I prefer this, but apparently this shouldn't be encapsulated in a data-related model?
# models.py
class Commission(models.Model):
check = ForeignKey
payment = ForeignKey
# other fields
#staticmethod
def run_commissions():
# do stuff
Static methods are used for grouping related functions in one class (mostly for factory methods), beside that, there is no difference between static method and function.
BUT. In your example you are assigning behavior to DATABASE model. DATABASE models are not LOGIC models, and you should separate them from your app logic. Anyway, controllers is also a bad name in that matter.
I'm not sure what process_entry does, but if it's only changing one Entry entity, then it can be named: Entry.process(), but NOT Entry as DB model! just another Entry class. However if that function does more than just changing Entry, then it shouldn't be assigned to Entry entity, but made as a service function.
Related
I have Product as a model.
I am refactoring some code, and there is a recurring query spread all over the code base which needs replacement.
So I'd like to centralize this query, in order to encapsulate its logic.
I'd like something like
<an_object>.get_uncompleted_products(products);
In this case, preserving existing code, products is already a result of a query (products = Products.objects.filter(filter_expression))
This is just a convenience question, I know a possible answer, but where would you put get_uncompleted_products(), what could be a good"django-way" solution?
I was initially wanting to put it on the Product model. But I was thinking Product methods to work directly on a single model reference, thus the signature would need to be:
class Product(models.Model):
#classmethod
get_uncompleted_products(list)
I am not sure why this gives me the feeling to be not so appropriate. A possible alternative would be to put it into a utility module. I could also have it in the view module but it seems it's used profusely in other views as well so I'd prefer somewhere more generic.
I guess the 'django way' would be to define it as a custom manager method, instead of class method, which can be shared between different models with composition instead of inheritance in case of class methods.
from django.db import models
class ProductQuerySet(models.query.QuerySet):
def get_uncompleted_products(self):
...
class ProductManager(models.Manager):
def get_queryset(self):
return ProductQueryset(self.model, using=self._db)
def get_uncompleted_products(self):
# defined twice to resolve queryset chaining issue with custom managers
return self.get_queryset().get_uncompleted_products()
class Product(models.Model):
...
objects = ProductManager()
I got huge Django App with huge amount of sub-applications. Currently, i'm working on reworking of models in one subapp, so i got a... problem.
I have huge amount of separated ugly functions to work with model. Basicly, it is a someting like:
def get_some_things(...):
def postprocess(...):
pass
def preprocess(...):
pass
preprocess(...)
x = MyModel.objects.....get(1)
return postprocess(x, ...)
And i got A LOT of functiongs like this and it's really ugly! And it's ugly used in current code(like DatabaseAccessor.get_db().django_kitty().get_some_things(...)). So, my idea was to make developers able to use these functions like this:
MyModel.get_some_things(...)
Or even like this:
MyModel.objects.get_some_things(...)
But! I got soo many functions so i can't write it inside model.py. So, i got few ideas:
Create model_mymodel.py and define my model with all functions inside it and static functions. But... I'm not sure, do i need to put it in model class?
Create mymodel_manager.py and create model manager for mymodel, define functions here. But... some of my "functions" should return just dicts, lists or even numbers. I wonder, is it ideologically wrong to make model manager be able to return something except the QuerySet?
Override __getattr__ of the MyModel class and dynamicly load modules like functions_common.py, functions_things.py etc, store gathered functions into dictionary and call needed?
If your models require a lot of unique methods, having huge model definitions is the price you pay. If for some reason you do want to split the functionality to other files (or, for something actually useful, share common functionality) you could use a mixin approach:
#mymodel.py
class MyModelMixin:
def django_kitty(self, ...):
pass
def postprocess(self, ...):
pass
def preprocess(self, ...):
pass
#models.py
from mymodel import MyModelMixin
class MyModel(models.Model, MyModelMixin):
pass
Regarding your own suggestions:
1 - If you want separate files per model, you could use this approach:
myapp/
models/
__init__.py
from mymodel import MyModel
mymodel.py
Beware that you would need to explicitly set the app_label for each model:
#mymodel.py
class MyModel(models.Model):
...
class Meta:
app_label = 'myapp'
2 - The return value type of manager methods is irrelevant. The distinguishing point of managers and models is to separate table and row level functionality respectively.
3 - Sounds like unnecessary magic, overriding Model.__getattr__ is a painful task.
Sounds like option 2 is the way to go.
There is nothing wrong with a model manager method not returning a QuerySet.
The guiding principle I use for model functions versus model managers is this: I use method functions on models regularly as long as the objective is to act on a single model object. However if my use case required me to handle multiple objects of the model and meets my DRY incentive, I would use a model manager for that.
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.
I have a Django model storing some infrequently changed but often used data. I'm looking for a pattern so that I can refer to them as kind of static class variables, eg, like SomeModel.Bar or SomeModel.Baz.
At the moment I'm using a staticmethod like so:
class SomeModel(models.Model):
name = models.CharField(max_length=100)
#staticmethod
def Baz():
#retrieve from cache or lookup Baz item
return baz
Meaning I refer to the items as SomeModel.Baz() but this just doesn't quite feel right. I just get the impression that I'm doing something wrong. I don't want to make it a property, as I don't want an instance to refer to the item.
Can anyone point me at a pattern or example to show me is I can implement a class level property in this way? Or tell me why I should be doing something totally different? Thanks :).
If you want some logic for model which doesn't belong to particular row, write custom model manager.
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