Some calculations became too complex to maintain inside my model, so I decided to move them out and break the code in several classes and modules.
There's a single class serving as a facade which I would like to have available in a model instance to pass data to it.
It is being constructed from model instance:
class Calculator:
def __init__(self, field1: date, field2: float):
self.field1= field1
self.field2 = field2
#classmethod
def from_django_model(cls, django_model_instance):
field1 = django_model_instance.field1
field2 = float(django_model_instance.field2)
Currently I call it inside each property on my model like so:
class DjangoModel(models.Model):
# initialize the calculator
def calculator(self, group):
return calculator.Calculator.from_django_model(self)
# use it
#cached_property
def calculated_field(self):
try:
return self.calculator().calculation_method
except AttributeError:
return "Field not set!"
I feel this is not a good solution, since now on multiple methods I'm initializing the calculator object multiple times.
I would like to construct it once when the model is initialized and then pass it to the model instance.
I tried doing this with model manager, but the model instance is not available with it.
Related
I have a class that other classes herit from:
class BaseManager:
model = Base
#classmethod
def get_by_id(cls, db: Session, id: int):
return db.query(cls.model).filter(cls.model.id == id).one()
class UserManager(BaseManager):
model = User
And it is used this way:
user = UserManager.get_by_id(db=db, id=user_id)
But the UserManager is ignoring the model defined in it, using the Base defined on BaseManager. How can I proceed?
I have already saw implementations such as django forms using metaclass and for example this question over here Access child class variable in parent class but I think that they don't apply to this context, i tried and did not succeed.
I have a table to store view events, that is, if a user views an entity, a record will be stored into that table. This table is represented by a model that has a generic relation, that is, it can be related to any other model.
I have defined a mixin ViewTracked that should be extended by any model that can be tracked (i.e. class SomeModel(ViewTracked)).
I want to have a custom method for queryset of objects manager called custom_method for example. I know that I can define a custom Manager and override the objects manager with it easily, but the problem is that the tracked model can already have a custom manager that has his own custom queryset, so I can't simply override it and lose the custom queryset that it has.
Unfortunately, I couldn't find a proper way of doing this, so I tried to add a metaclass to override the manager's get_queryset and add my custom method to it, but for some reason, when I call SomeModel.objects it always returns None.
Here's what I tried:
# Meta class
class ViewTrackedMeta(ModelBase):
def __new__(mcs, class_name, base_classes, attributes_dict):
# let ModelBase do its magic
new_class = super().__new__(mcs, class_name, base_classes, attributes_dict)
if hasattr(new_class, 'objects'):
objects_manager = new_class.objects
if isinstance(objects_manager, Manager):
queryset = objects_manager.get_queryset()
def custom_method(queryset):
return queryset.filter(...)
def get_extended_queryset(manager):
queryset.custom_method = types.MethodType(custom_method, queryset)
objects_manager.get_queryset = types.MethodType(get_extended_queryset, objects_manager)
return new_class
# Mixin
class ViewTracked(Model, metaclass=ViewTrackedMeta):
class Meta:
abstract = True
...
# Models
class SomeModel(ViewTracked):
objects = CustomManager()
class SomeOtherModel(ViewTracked):
... # default django objects manager
class SomeOtherModel(ViewTracked):
objects = OtherCustomManager()
Is there any other way I can achieve what I want? Why SomeModel.objects is always returning None?
Other than instaniating your manager classes, you should be using from_queryset. Here are the docs.
class CustomQuerySet(models.QuerySet):
def manager_and_queryset_method(self):
return
class MyModel(models.Model):
objects = models.Manager.from_queryset(CustomQuerySet)()
Now you can do:
MyModel.objects.manager_and_queryset_method()
as well as
MyModel.objects.filter(something="else").manager_and_queryset_method()
I am generating a Django model based on an abstract model class AbstractAttr and a normal model (let's say Foo).
I want my foo/models.py to look like this:
from bar.models import Attrs
# ...
class Foo(models.Model):
....
attrs = Attrs()
In the Attrs class which mimics a field I have a contribute_to_class that generates the required model using type(). The generated model c is called FooAttr.
Everything works. If I migrate, I see FooAttr appear in the proper table.
EXCEPT FOR ONE THING.
I want to be able to from foo.models import FooAttr. Somehow my generated FooAttr class is not bound to the models.py file in which it is generated.
If I change my models.py to this:
class Foo(models.Model):
# ...
FooAttr = generate_foo_attr_class(...)
it works, but this is not what I want (for example, this forces the dev to guess the generate class name).
Is what I want possible, define the class somewhat like in the first example AND bind it to the specific models.py module?
The project (pre-Alpha) is here (in develop branch):
https://github.com/zostera/django-mav
Some relevant code:
def create_model_attribute_class(model_class, class_name=None, related_name=None, meta=None):
"""
Generate a value class (derived from AbstractModelAttribute) for a given model class
:param model_class: The model to create a AbstractModelAttribute class for
:param class_name: The name of the AbstractModelAttribute class to generate
:param related_name: The related name
:return: A model derives from AbstractModelAttribute with an object pointing to model_class
"""
if model_class._meta.abstract:
# This can't be done, because `object = ForeignKey(model_class)` would fail.
raise TypeError("Can't create attrs for abstract class {0}".format(model_class.__name__))
# Define inner Meta class
if not meta:
meta = {}
meta['app_label'] = model_class._meta.app_label
meta['db_tablespace'] = model_class._meta.db_tablespace
meta['managed'] = model_class._meta.managed
meta['unique_together'] = list(meta.get('unique_together', [])) + [('attribute', 'object')]
meta.setdefault('db_table', '{0}_attr'.format(model_class._meta.db_table))
# The name of the class to generate
if class_name is None:
value_class_name = '{name}Attr'.format(name=model_class.__name__)
else:
value_class_name = class_name
# The related name to set
if related_name is None:
model_class_related_name = 'attrs'
else:
model_class_related_name = related_name
# Make a type for our class
value_class = type(
str(value_class_name),
(AbstractModelAttribute,),
dict(
# Set to same module as model_class
__module__=model_class.__module__,
# Add a foreign key to model_class
object=models.ForeignKey(
model_class,
related_name=model_class_related_name
),
# Add Meta class
Meta=type(
str('Meta'),
(object,),
meta
),
))
return value_class
class Attrs(object):
def contribute_to_class(self, cls, name):
# Called from django.db.models.base.ModelBase.__new__
mav_class = create_model_attribute_class(model_class=cls, related_name=name)
cls.ModelAttributeClass = mav_class
I see you create the model from within models.py, so I think you should be able to add it to the module's globals. How about this:
new_class = create_model_attribute_class(**kwargs)
globals()[new_class.__name__] = new_class
del new_class # no need to keep original around
Thanks all for thinking about this. I have updated the source code of the project at GitHub and added more tests. See https://github.com/zostera/django-mav
Since the actual generation of the models is done outside of foo/models.py (it takes place in mav/models.py, it seems Pythonically impossible to link the model to foo/models.py. Also, after rethinking this, it seems to automagically for Python (explicit is better, no magic).
So my new strategy is to use simple functions, a decorator to make it easy to add mav, and link the generated models to mac/attrs.py, so I can universally from mav.attrs import FooAttr. I also link the generated class to the Foo model as Foo._mav_class.
(In this comment, Foo is of course used as an example model that we want to add model-attribute-value to).
I am writing some python gui app (PySide to be exact) and I am using my own class to handling DB. What's the correct way to use models? Currently I have something like this:
class DB(object):
def __init__(self, dbfile):
some db connect work
def updateEntry(entryid):
some update query etc
def getEntry(entryid):
fetching entry from db
def createEntry(entryvalue):
insert entry
class EntryModel(object):
def __init__(db,entryid=None,entryvalue=None):
self.db=db
self.entryid=entryid
self.entryvalue=entryvalue
if entryid is None:
self.db.createEntry(self.entryvalue)
elif self.entryvalue is None:
self.db.getEntry(self.entryid)
def some_func(self):
some other work
And it's working just fine... But I have a feeling that something is wrong here... I mean, I have to pass DB to each model, I don't think that's correct way. How to do it in proper way without using frameworks like SQLAlchemy and so on?
You can at least create a base class, let's called it Model (like in Django, or Base as it is called in SQLAlchemy)
We'll keep a reference to the db object as a class attribute so it is the same for all instances, and inherited so you don't have to pass it around
class Model(object):
db = None # This var is a class attribute
#classmethod
def init_db(cls):
cls.db = your_code_to_create_db()
class Entry(Model):
def __init__(self, entry_id, entry_value):
self.entry_id = entry_id
self.entry_value = entry_value
super(Entry, self).__init__()
def save(self):
# Use db here
self.db
# To use
Model.init_db() # Inits the one db var for the class
entry = Entry(...)
entry.save()
I hope you see the idea and adapt it to your needs!
With an instance of Concert I get: unbound method do_stuff() must be called with Concert instance as first argument (got ModelBase instance instead)
models.py:
class Event(models.Model):
def do_stuff(self):
response self.do_specific_stuff(self)
class Concert(Event):
def do_specific_stuff(self):
...
class Party(Event):
def do_specific_stuff(self):
...
views:
def index(request):
x = Concert.objects.get(name='Jack White # Oakland')
output = x.do_stuff()
return HttpResponse(output)
My goal is to loop trough all the events and execute the do_specific_stuff child class method based on what kind of event it is.
In Django, inheritance triggers multi-table inheritance, but you don't get the polymorphism in Python. It's just an instance of the ORM not providing a perfect correspondence between the data schema and the object model.
In other words, when you query Event, you get back a whole bunch of Event objects, regardless of whether some of them are actually Concert or Party objects. You have to manually downcast. If an Event is a Concert, it will have an attribute called concert, which points to the corresponding Concert subclass. Likewise for Party. If it is just a normal Event, it will have neither attribute.
You could use the following property on Event to automatically downcast your object:
#property
def as_child_class(self):
"""Casts this object to its subclass, if possible"""
if hasattr(self, 'concert'):
return self.concert
elif hasattr(self, 'party'):
return self.party
else:
return self
Then you could do something like:
for event in Event.objects.all()
event.as_child_class.do_specific_stuff()
Similar questions have come up before:
Polymorphism in Django
How do I access the child classes of an object in django without knowing the name of the child class?
And this link has some other ideas:
http://jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/
It seems to me that your Event model is ONLY for inheritance, so you should abstract it:
class Event(models.Model):
class Meta:
absract = True
def do_stuff(self):
response self.do_specific_stuff()
def do_specific_stuff(self):
raise NotImplemented
class Concert(Event):
def do_specific_stuff(self):
...
class Party(Event):
def do_specific_stuff(self):
...
I may be wrong about your usage of Event, but if it was, abstracting your Event model will have it to be like a plain class, I mean no database actions will be taken for such model.
Hope this helps! :)
First of all, see Template method
Secondly, the Event class should be abstract.
class Event:
def __init__:
raise NotImplemented('This class is abstract')
Thirdly, see Single Table Inheritance and Class Table Inheritance patterns.
And django-ORM realisation of the patterns there
Good luck =)