I'm creating an instance of a model (Container), and it seems like the pre_save function is not triggered.
This is the class in the 'signals':
class ContainerCreatedMixin(object):
#staticmethod
#receiver(pre_save, sender=Container)
def container_pre_save(sender, instance, **kwargs):
# create container folder if not created yet
if instance.folder_created_at is None:
is_folder_created = ContainerCreatedMixin().create_folder(instance)
if is_folder_created:
instance.folder_created_at = now()
def create_virtual_folder(self, container):
try:
......
Using the receiver decorator on a class method doesn't really make sense.
Put your decorated method out of class and it should be registered if the file is imported. Also, there is no need of creating Mixings for the following.
Related
So I have this class that helps me override the update method of a queryset:
class QuerySetUpdateOverriden(QuerySet, object):
def update(self, *args, **kwargs):
super().update(*args, **kwargs)
if hasattr(self, 'method_from_object'):
self.method_from_object()
return
and here's my class where I use it:
class MyObject:
objects = QuerySetUpdateOverriden.as_manager()
def method_from_object(self):
print("called")
the print statement is never reached.
And I get why - the objects field doesn't inherit MyObject too.
So, the question is - how can I make it inherit MyObject so method_from_object will be called?
Thanks.
You test if self has a method called 'method_from_object', but your QuerySetUpdateOverriden has no method call like this. And MyObject does not inherit from QuerySetUpdateOverriden.
This code would be work i think:
class MyObjectManager(QuerySetUpdateOverriden.as_manager()):
def method_from_object(self):
print("called")
class MyObject(models.Model):
objects = QuerySetUpdateOverriden.as_manager()
Following up on this question Flask-Admin Role Based Access - Modify access based on role I don't understand how to implement role-based views, especially regarding the form and column_lists.
Say I want MyModelView to show different columns if the user is a regular user or a superuser.
Overriding is_accessible in MyModelView has no effect at all
from flask_security import Security, SQLAlchemyUserDatastore, current_user
class MyModelView(SafeModelView):
# ...
def is_accessible(self):
if current_user.has_role('superuser'):
self.column_list = superuser_colum_list
self.form_columns = superuser_form_columns
else:
self.column_list = user_colum_list
self.form_columns = user_form_columns
return super(MyModelView, self).is_accessible()
# Has same effect as
def is_accessible(self):
return super(MyModelView, self).is_accessible()
and defining conditional class attributes does not work either as current_user is not defined (NoneType error as per AttributeError on current_user.is_authenticated()). Doing the same in the ModelView's __init__ being equivalent, current_user is still not defined
class MyModelView(SafeModelView):
#[stuff]
if current_user.has_role('superuser'):
column_list = superuser_colum_list
form_columns = superuser_form_columns
else:
column_list = user_colum_list
form_columns = user_form_columns
#[other stuff]
FYI, SafeModelView can be any class inheriting from dgBaseView in the previously mentioned question.
I usually define view class attributes such as column_list as properties. It allows you to add some dynamic logic to them:
from flask import has_app_context
from flask_security import current_user
class MyModelView(SafeModelView):
#property
def column_list(self):
if has_app_context() and current_user.has_role('superuser'):
return superuser_column_list
return user_column_list
#property
def _list_columns(self):
return self.get_list_columns()
#_list_columns.setter
def _list_columns(self, value):
pass
The problem with using this approach (and why your reassigning of column_list values in is_accessible function took no effect) is that many view attributes are cached on application launch and stored in private attributes. column_list for example is cached in _list_columns attribute so you need to redefine it as well. You can look how this caching works in flask_admin.model.base.BaseModelView._refresh_cache method.
Flask has_app_context method is needed here because first column_list read is happened on application launch when your current_user variable has no meaningful value yet.
The same can be done with form_columns attribute. The properties will look like this:
#property
def form_columns(self):
if has_app_context() and current_user.has_role('superuser'):
return superuser_form_columns
return user_form_columns
#property
def _create_form_class(self):
return self.get_create_form()
#_create_form_class.setter
def _create_form_class(self, value)
pass
#property
def _edit_form_class(self):
return self.get_edit_form()
#_edit_form_class.setter
def _edit_form_class(self, value):
pass
I have a class with a custom pk field, and a classmethod to generate this special pk:
class Card(models.Model):
custom_pk = models.BigIntegerField(primary_key=True)
other_attr = ...
#classmethod
def gen_id(cls):
# ...
return the_id
Now, I suppose I can create object (in a view for example) doing this:
Card.objects.create(custom_pk=Card.gen_id(), other_attr="foo")
But I'd like to have the same result using the classic way to do it:
Card.objects.create(other_attr="foo")
Thanks!
You can use pre_save signal to supply your primary key is missing. This signal handler will be called before each call to Card.save() method, therefore we need to make sure that we won't override custom_pk if already set:
#receiver(pre_save, sender=Card)
def add_auto_pk(sender, instance, **kwargs):
if not instance.custom_pk:
instance.custom_pk = Card.get_id()
See Django signal documentation for more details.
I am trying to define a "before_save" method in certain classes in my django 1.2 project. I'm having trouble connecting the signal to the class method in models.py.
class MyClass(models.Model):
....
def before_save(self, sender, instance, *args, **kwargs):
self.test_field = "It worked"
I've tried putting pre_save.connect(before_save, sender='self') in 'MyClass' itself, but nothing happens.
I've also tried putting it at the bottom of the models.py file:
pre_save.connect(MyClass.before_save, sender=MyClass)
I read about connecting signals to class methods here, but can't figure out the code.
Anybody know what I'm doing wrong?
A working example with classmethod:
class MyClass(models.Model):
#....
#classmethod
def before_save(cls, sender, instance, *args, **kwargs):
instance.test_field = "It worked"
pre_save.connect(MyClass.before_save, sender=MyClass)
There's also a great decorator to handle signal connections automatically: http://djangosnippets.org/snippets/2124/
I know this question is old, but I was looking for an answer to this earlier today so why not. It seems from your code that you actually wanted to use an instance method (from the self and the field assignment). DataGreed addressed how to use it for a class method, and using signals with instance methods is pretty similar.
class MyClass(models.Model)
test_field = models.Charfield(max_length=100)
def __init__(self, *args, **kwargs):
super(MyClass, self).__init__(*args, **kwargs)
pre_save.connect(self.before_save, sender=MyClass)
def before_save(self, sender, instance, *args, **kwargs):
self.test_field = "It worked"
I'm not sure if this is a good idea or not, but it was helpful when I needed an instance method called on an object of class A before save from class B.
Rather than use a method on MyClass, you should just use a function. Something like:
def before_save(sender, instance, *args, **kwargs):
instance.test_field = "It worked"
pre_save.connect(before_save, sender=MyClass)
I have a Django app with custom form fields, some of which have slow operations in their constructors. I was surprised recently to find out that those constructors were getting called when Django itself was starting up, even before a user does something that requires that form in a view.
Why are they getting instantiated at server start?
Example:
urls.py:
from myapp.views import view1
...
url(r'^test$', view1.test),
views/view1.py:
class MyForm(ModelForm):
class Meta:
model = MyModel
field1 = MyChoiceField()
class MyChoiceField(ChoiceField):
def __init__(self, choices=(), required=True, widget=None, label=None,
initial=None, help_text=None, *args, **kwargs):
super(ChoiceField, self).__init__(required, widget, label, initial,
help_text, *args, **kwargs)
self.choices = [(m.id, m.name) for m in ReallyLargeTableModel.objects.all()]
If I set a break point inside that field constructor, then start up Django, it breaks the first time I request any page, even if the view in question does not need that form or field. The stacktrace leads back to the import line in urls.py.
Is this because I'm importing view1 in urls.py, instead of importing view1.test?
Edit: This isn't Django specific, here is a test case the illustrates the behavior:
class Something():
def __init__(self):
print "Something __init__() called"
class UsesSomething():
field = Something()
If you run this in the interactive terminal, it will print "Something init() called". This was surprising to me because I have not actually instantiated a UsesSomething object.
Because you instantiate the fields in the form definition, which is presumably being imported by one of your views.
The field init is the wrong place to do this sort of dynamic initialization, for this exact reason. You want something that is called when the form is initialized: ie, the form's __init__.
That said, you don't actually want to do this at all - you just need to use forms.ModelChoiceField, which takes a queryset and does the dynamic assignment of choices for you.
class MyForm(ModelForm):
field1 = forms.ModelChoiceField(queryset=ReallyLargeTableModel.objects.all())
In your example:
class UsesSomething():
field = Something()
The line of code field = Something() will execute when you import the containing module as Python processes the class definition. This is just how Python works. You can actually put arbitrary code inside a class definition.
module: test.py:
class UsesSomething():
print "wow!"
>>> import test
wow!