Getting a property from super() in Python which may change - python

I am trying to build a framework and I am having problems with inheritance of properties between classes. I have a folder called framework with a init.py as follows:
from management import projects
from models import model
projects = projects()
model = model()
Then management looks like:
class projects(object):
def __init__(self):
self._current = None
#property
def current(self):
return self._current
def set_current(self, name):
"""setting a project by name, then self._current = name"""
Finally models:
class model(projects):
def __init__(self):
super(model, self).__init__()
#property
def current(self):
return super().current
Then when I try to use the framework from other script:
import framework as fw
fw.projects.set_current('foo')
fw.model.current
It returns None instead of 'foo'. I am pretty sure that the error is in the init file from framework where the instances are done before setting a project, I try some differents things but I don't have a clue. How can models class notice the changes in projects class?? Furthermore, I do not really want that model has the property current but I need its value for other methods (anyways I do not care if models has this property if it works as I expected). Thanks!!
EDIT:
For now I will store some metadata in the backend to access it from any class. But I am still wondering if there is a more elegant solution.

From my humble experience dealing intensively with Python in these last months, I would say it is absurd trying to access a children's property from the parent class because in this case, they are different instances.
projects = projects()
model = model()
Here you are creating two different instances and they do not share anything, so changes made in project won't be visible to model. I would recommend using composition over inheritance and inject a Project dependency onto Model.
Model class
class Model:
def __init__(self, project):
self.project = project
def get_current(self):
return self.project.get_current()
Project class
class Project:
# in Python 3 there is no need to use the implicit-object inheritance
def __init__(self):
self.current = None
def get_current(self):
return self.current
def set_current(self, value):
self.current = value
Playing with them
from framework.Model import Model
from framework.Project import Project
project = Project()
model = Model(project)
project.set_current("Example")
print(model.get_current()) # Example
I hope this helps.
Tomás.

Related

Best practices for python abstract class

I came across this code in a codebase:
class Worker(object):
state_names = {}
def __init__(self, status_value):
self.status_value = status_value
#property
def state(self):
return self.status_value.value
#state.setter
def state(self, status):
self.ui.debug('state: {} -> {}'
''.format(self.state_name(), self.state_name(status)))
self.status_value.value = status
def state_name(self, s=None):
return self.state_names[s or self.state]
There are some obvious problems with it:
self.state_names is read-only and empty
self.ui does not exist
And after looking around for awhile, I realized that there was a single class that derived from this and was known to have self.ui and provided self.state_names.
I think it is kind of odd to implement an abstract class in this way, where the virtual member functions have implementations that assume data members in the derived class.
Is there a better, cleaner way to do this development pattern in python? I am going to assume that AbstractBaseClass is a starting point.

Add a dynamically generated Model to a models.py in an Django project

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).

Python, DB and models

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!

Python - Accessing subclasses' variables from parent class while calling classmethods

i'm trying to build sort of a "mini django model" for working with Django and MongoDB without using the norel Django's dist (i don't need ORM access for these...).
So, what i'm trying to do is to mimic the standart behavior or "implementation" of default models of django... that's what i've got so far:
File "models.py" (the base)
from django.conf import settings
import pymongo
class Model(object):
#classmethod
def db(cls):
db = pymongo.Connection(settings.MONGODB_CONF['host'], settings.MONGODB_CONF['port'])
#classmethod
class objects(object):
#classmethod
def all(cls):
db = Model.db() #Not using yet... not even sure if that's the best way to do it
print Model.collection
File "mongomodels.py" (the implementation)
from mongodb import models
class ModelTest1(models.Model):
database = 'mymongodb'
collection = 'mymongocollection1'
class ModelTest2(models.Model):
database = 'mymongodb'
collection = 'mymongocollection2'
File "views.py" (the view)
from mongomodels import ModelTest1, ModelTest2
print ModelTest1.objects.all() #Should print 'mymongocollection1'
print ModelTest2.objects.all() #Should print 'mymongocollection2'
The problem is that it's not accessing the variables from ModelTest1, but from the original Model... what's wrong??
You must give objects some sort of link to class that contains it. Currently, you are just hard-coding it to use Model()s atttributes. Because you are not instantiating these classes, you will either have to use either a decorator or a metaclass to create the object class for you in each subclass of Model().

Implementing an inline to represent a ListField in Django-nonrel

Is it possible to use something similar to the inline relational items from the Django admin to represent embedded models in a ListField?
For Example, I've got the following models:
class CartEntry(model.Model):
product_name=model.CharField( max_length=20 )
quantity = model.IntegerField()
class Cart(model.Model):
line_items = ListField(EmbeddedModelField('CartEntry'))
I've tried using the standard inlining, but I know it's not right:
class CartEntryInline( admin.StackedInline ):
model=CartEntry
class CartAdmin(admin.ModelAdmin)
inlines=[CartEntryInline]
But obviously that doesn't work, since there's no foreign key relation. Is there any way to do this in django-nonrel?
This is not so easy to do out of the box. You will need to manage ListField and EmbeddedModelField type fields in Django's admin module and do some hacking to get it done. You'll have to implement two parts:
Use EmbeddedModelField in Django's admin
You need to define a class that handles EmbeddedModelField objects to make it work with Django's admin. Here is a link where you can find great sample codes. Below are just code blocks for demonstration:
Add this class into your models.py file and use EmbedOverrideField instead of EmbeddedModelField in Cart model:
class EmbedOverrideField(EmbeddedModelField):
def formfield(self, **kwargs):
return models.Field.formfield(self, ObjectListField, **kwargs)
Implement a class in forms.py that has two methods:
class ObjectListField(forms.CharField):
def prepare_value(self, value):
pass # you should actually implement this method
def to_python(self, value):
pass # Implement this method as well
Use ListFields in Django's admin
You also need to define a class that handles ListField objects to make it work with Django's admin. Here is a link where you can find great sample codes. Below are just code blocks for demonstration:
Add this class into your models.py file and ItemsField instead of ListField in Cart model:
class ItemsField(ListField):
def formfield(self, **kwargs):
return models.Field.formfield(self, StringListField, **kwargs)
Implement a class in forms.py that has two methods:
class StringListField(forms.CharField):
def prepare_value(self, value):
pass # you should actually implement this method
def to_python(self, value):
pass # Implement this method as well

Categories

Resources