I'd like to know if it is possible to retrieve attributes used by a method.
Here is a sample to explain what I am trying to do:
class Foobar(object):
bar = 123
#property
def foo(self):
return self.baz * self.bar
def get_foobar(self):
return '{} * {} = {}'.format(self.baz, self.bar, self.foo)
I would like to be able to know that calling Foobar().get_foobar() will require self.baz, self.bar and self.foo to be set, without actually calling it.
My current approach is to add an attribute to get_foobar:
def get_foobar(self):
return '{} * {} = {}'.format(self.baz, self.bar, self.foo)
get_foobar.requires = ['baz', 'bar', 'foo']
However, I think it is a bit redundant and might be error-prone.
What is the point of doing this?
You may wonder why I'd like to achieve this behavior.
In my concrete case, Foobar is actually a django model. And attributes are actually fields retrieve from the database. I created a View mixin that enables me to specify the fields needed by the view. For instance:
class SomeUserView(ModelMixin, View):
model = User
fields = [
'username', 'groups__name', 'permissions__id',
'orders__products__name', 'orders__products__price',
]
With a few introspection, I can build the query that will retrieve all and only the data needed by the view. In this case, it would look like:
User.objects.all().only(
'username', 'groups__name', 'permissions__id',
'orders__products__name', 'orders__products__price',
).select_related('groups', 'permissions', 'orders__products')
Now, the fields attribute may contain not only fields, but also instance methods, which may require fields that are not listed. Let's say I have:
class User(models.Model):
def __str__(self):
return '{} ({})'.format(self.username, self.email)
__str__.requires_fields = ['username', 'email']
class Permission(models.Model):
def __str__(self):
return self.name
__str__.requires_fields = ['name']
class SomeUserView(ModelMixin, View):
model = User
fields = [
'username', 'groups__name', 'permissions', '__str__',
'orders__products__name', 'orders__products__price',
]
Then, the query will be:
User.objects.all().only(
'username', 'groups__name', 'permissions__name', 'email',
'orders__products__name', 'orders__products__price',
).select_related('groups', 'permissions', 'orders__products')
This works, however I would like to avoid setting the requires_fields attribute to each methods and have to update it carefully every time the method is revised.
I have no great hope in this being possible, but still asking.
As an alternative, I think I could write a decorator, such as:
class Wrapper:
def __init__(self, obj, fields):
self.obj = obj
self.fields = set(fields)
self._used_fields = set()
def __getattribute___(self, name):
if name not in self.fields:
raise AttributeError(
"You forgot to set '{}' as required field".format(name))
self._used_fields.add(name)
return getattr(self.obj, name)
def exit_method(self):
if self.fields != self._used_fields:
warnings.warn(
"Make sure the following fields are actually needed: {}".format(
self.fields - self._used_fields))
def requires_fields(*fields):
def decorator(func):
def inner(self, *args, **kwargs):
self_wrapper = Wrapper(self, fields)
func(self_wrapper, *args, **kwargs)
self_wrapper.exit_method()
inner.__name__ = func.__name__
inner.requires_fields = fields
return decorator
#requires_fields('baz', 'bar', 'foo')
def get_foobar(self):
return '{} * {} = {}'.format(self.baz, self.bar, self.foo)
So that I can ease "mistakes detection". But it looks quite odd. :D
I would like to be able to know that calling Foobar().get_foobar()
will require self.baz, self.bar and self.foo to be set, without
actually calling it.
No, of course not, in the general case. Here's my foobar:
return self.foo if isHalting() else self.bar
return eval("self.foo")
But more realistically, you have several options:
Compile the syntax tree. Check for the field nodes (or whichever you're interested in) then see what their value is. This can be time consuming. Familiarize yourself with the visitor pattern and this can be a pretty powerful way to parse; this is how a static language like C or Java could raise a compilation error that a variable will be undefined*.
Check for closures with function.get.__closure__ This won't help you retrieve all the fields, but may help you figure out which fields have been bound to the function, which may be relevant in some cases
The best option, is to use try/except statements. Style them however you want, but for example
def foo():
s = ''
try:
s += str(self.baz)
except: raise NoBazError("No Baz!")
To me, this is the best option because it's the most explicit and straightforward.
* That would be tough for even a statically typed language if you're using fields, but local variables can be checked for initialization, sometimes
Related
I have a serializer for a model with an image field, for which I have saved multiple different sized thumbnail images.
I access them by returning their URL using the SerializerMethodField:
class GalleryImageSerializer(serializers.ModelSerializer):
image_sm = serializers.SerializerMethodField()
image_md = serializers.SerializerMethodField()
image_lg = serializers.SerializerMethodField()
image_compressed = serializers.SerializerMethodField()
def get_image_sm(self, obj):
return default_storage.url(f'{splitext(obj.image.name)[0]}/sm.jpg')
def get_image_md(self, obj):
return default_storage.url(f'{splitext(obj.image.name)[0]}/md.jpg')
def get_image_lg(self, obj):
return default_storage.url(f'{splitext(obj.image.name)[0]}/lg.jpg')
def get_image_compressed(self, obj):
return default_storage.url(f'{splitext(obj.image.name)[0]}/compressed.jpg')
This code works, but it kind of violates the "don't repeat yourself" guideline.
As you can see, these are all duplicate SerializerMethodFields, with the only difference being the filename, eg 'lg.jpg', 'md.jpg', etc.
I'd much prefer to have only one function that I call with an argument for the filename, as an example(pseudocode):
class GalleryImageSerializer(serializers.ModelSerializer):
image_sm = serializers.SerializerMethodField(filename='sm.jpg')
image_md = serializers.SerializerMethodField(filename='md.jpg')
image_lg = serializers.SerializerMethodField(filename='lg.jpg')
image_compressed = serializers.SerializerMethodField(filename='compressed.jpg')
def get_image(self, obj, filename=''):
return default_storage.url(f'{splitext(obj.image.name)[0]}/{filename}')
Currently I am unable to find any way to achieve this. Reading the source code of SerializerMethodField, it doesn't seem to support it.
Is there any way to avoid creating duplicate functions for fields with arbitrary differences?
You can add these fields in the to_representation method.
def to_representation(self, instance):
ret = super().to_representation(instance)
# add img urls to ret dict
for name in ['sm', 'md', 'lg', 'compressed']:
ret['image_' + name] = default_storage.url(f'{splitext(instance.image.name)[0]}/{name}.jpg')
return ret
check the docs for more details:
https://www.django-rest-framework.org/api-guide/serializers/#to_representationself-instance
I'm unit testing a view and I am attempting to patch the .data property on my serializer but it looks like it behaves differently when the many=True kwarg is passed to the serializer constructor and thus not properly patching. Here is a generalized example of my code.
# myapp/serializers.py
class MySerializer(serializers.Serializer):
some_field = serializers.CharField()
# myapp/views.py
class MyView(View):
def get(self, request):
# ..stuff
some_data = []
serializer = MySerializer(some_data, many=True)
print(type(serializer)) # <class 'rest_framework.serializers.ListSerializer'>
print(type(serializer.data)) # <class 'rest_framework.utils.serializer_helpers.ReturnList'>
return Response({"data": seralizer.data, status=200})
# in tests
def test_view_case_one(mocker):
# setup other mocks
serialized_data = mocker.patch("myapp.views.MySerializer.data", new_callable=mocker.PropertyMock)
# invoke view
response = MyView().get(fake_request)
# run assertions
serialized_data.assert_called_once() # this says it's never called
Earlier I had ran into issues attempting to patch rest_framework.serializers.ListSerializer.data. Must of been a typo. Reattempted and was able to successfully patch. Given the case many=True recreates the serializer as a ListSerializer I simply needed to patch the property on the underlying class.
serialized_data = mocker.patch(
"rest_framework.serializers.ListSerializer.data",
new_callable=mocker.PropertyMock
)
Edit: A more in depth answer
When many=True is used the __new__ method on BaseSerializer grabs you class and constructs a ListSerializer from it and that is why my object showed up as a ListSerializer. Since we are actually receiving a ListSerializer instead of our defined class the patch is not applied to ListSerializer.data method. The relevant parts of the source code for BaseSerializer is below
class BaseSerializer(Field):
def __new__(cls, *args, **kwargs):
# We override this method in order to automagically create
# `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
#classmethod
def many_init(cls, *args, **kwargs):
"""
This method implements the creation of a `ListSerializer` parent
class when `many=True` is used. You can customize it if you need to
control which keyword arguments are passed to the parent, and
which are passed to the child.
Note that we're over-cautious in passing most arguments to both parent
and child classes in order to try to cover the general case. If you're
overriding this method you'll probably want something much simpler, eg:
#classmethod
def many_init(cls, *args, **kwargs):
kwargs['child'] = cls()
return CustomListSerializer(*args, **kwargs)
"""
allow_empty = kwargs.pop('allow_empty', None)
child_serializer = cls(*args, **kwargs)
list_kwargs = {
'child': child_serializer,
}
if allow_empty is not None:
list_kwargs['allow_empty'] = allow_empty
list_kwargs.update({
key: value for key, value in kwargs.items()
if key in LIST_SERIALIZER_KWARGS
})
meta = getattr(cls, 'Meta', None)
list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
return list_serializer_class(*args, **list_kwargs)
When working with Django model forms, I often do something like this:
def my_view(request):
new_entry = MyModel(name='a')
form = MyModelForm(instance=new_entry)
...
I want to do something similar with a modelformset. Something like this would be ideal:
def my_view(request):
MyFormSet = modelformset_factory(MyModel, form=MyModelForm)
new_entries = [MyModel(name='a'), MyModel(name='b')]
formset = MyFormSet(instances=new_entries) # off course this does not work
...
Since the items are not stored in the database yet, I can't set the instances using a queryset. If I want to use initial I have to declare the fields in the form, which is not ideal and seems a bit hacky.
Any suggestions how I can set the instances of each modelform in a modelformset?
Ok, I think I've found a solution.
class FormSetWithInstances(BaseFormSet):
def __init__(self, *args, **kwargs):
self.instances = kwargs.pop('instances')
super(FormSetWithInstances, self).__init__(*args, **kwargs)
def get_form_kwargs(self, index):
form_kwargs = super(FormSetWithInstances, self).get_form_kwargs(index)
if index < len(self.instances):
form_kwargs['instance'] = self.instances[index]
return form_kwargs
Be careful when using this modelformsets or inlinemodelformsets, as the queryset will override the instance you set.
An alternative approach:
class FormSetWithInstances(BaseFormSet):
def get_form_kwargs(self, index):
kwargs = super(FormSetWithInstances, self).get_form_kwargs(index)
instances = kwargs.pop('instances')
try:
kwargs.update({'instance': instances[index]})
except IndexError:
pass
return kwargs
Then, when creating instances of FormSetWithInstances, you pass in a list of instances as a form kwarg:
form_set = FormSetWithInstances(form_kwargs={'instances': [...]})
I personally prefer this method because it takes advantage of existing class infrastructure instead of defining custom class members in an overridden __init__(). Also, it's in the docs.
I'm not aware of an easy way to pass a list of instances as you are trying to do. Here's a couple of options that might work depending on your use case.
You can provide initial data for the model formset. This should be a list of dictionaries, not model instances:
initial = [{'name': 'a'}, {'name': 'b'}]
formset = MyFormSet(
queryset=MyModel.objects.none(),
initial=initial,
)
Note that I have set the queryset to an empty queryset. If you didn't do this, then the formset would display existing instances, and the initial data would be used for new instances.
If you have initial values for fields that you do not wish to include in the form, then you could be able to set those values when you [save the formset].
instances = formset.save(commit=False)
names = ['a', 'b']
for instance, name in zip(instances, names):
instance.name = name
instance.save()
Is there any way to strip surrounding whitespace from all values in WTForms without adding a filter to every single field?
Currently I'm passing filters=[strip_whitespace] with the function shown below to my fields but having to repeat this for every field is quite ugly.
def strip_whitespace(s):
if isinstance(s, basestring):
s = s.strip()
return s
A solution requiring subclassing of Form would be fine since I'm already doing that in my application.
You can do it in WTForms 2.x by using the bind_field primitive on class Meta. The class Meta paradigm is a way to override WTForms behaviors in contexts such as binding/instantiating fields, rendering fields, and more.
Because anything overridden in class Meta defined on a Form is inherited to any form subclasses, you can use it to set up a base form class with your desired behaviors:
class MyBaseForm(Form):
class Meta:
def bind_field(self, form, unbound_field, options):
filters = unbound_field.kwargs.get('filters', [])
filters.append(my_strip_filter)
return unbound_field.bind(form=form, filters=filters, **options)
def my_strip_filter(value):
if value is not None and hasattr(value, 'strip'):
return value.strip()
return value
Now, just inherit MyBaseForm for all your forms and you're good to go.
Unfortunately, I have no enough reputation to comment first response.
But, there is extremely unpleasant bug in that example:
When you do filters.append(smth) then on each form initialization filters growth by 1 element.
As a result, your code works slower and slower until you restart it
Consider Example:
class MyBaseForm(Form):
class Meta:
def bind_field(self, form, unbound_field, options):
filters = unbound_field.kwargs.get('filters', [])
filters.append(my_strip_filter)
return unbound_field.bind(form=form, filters=filters, **options)
def my_strip_filter(value):
if value is not None and hasattr(value, 'strip'):
return value.strip()
return value
class MyCustomForm(MyBaseForm):
some_field = StringField(filters=[lambda x: x])
for i in range(100):
MyCustomForm(MultiDict({'some_field': 'erer'}))
print(len(MyCustomForm.some_field.kwargs['filters'])) # print: 101
So the fast fix is to check that this filter not in list:
class MyBaseForm(Form):
class Meta:
def bind_field(self, form, unbound_field, options):
filters = unbound_field.kwargs.get('filters', [])
if my_strip_filter not in filters:
filters.append(my_strip_filter)
return unbound_field.bind(form=form, filters=filters, **options)
I wouldn't be surprised if you could do it by subclassing form, but my solution was to just create custom Stripped* fields. I think this is at least better than passing filters every time because it is less error prone:
from wtforms import StringField, PasswordField
class Stripped(object):
def process_formdata(self, valuelist):
if valuelist:
self.data = valuelist[0].strip()
else:
self.data = ''
class StrippedStringField(Stripped, StringField): pass
class StrippedPasswordField(Stripped, PasswordField): pass
These 2 methods are equivalent.
method 1
class X(object):
a = 1
method 2
X = type('X', (object,), dict(a=1))
I want to know what is the equivalent of :
class ObjectTable(tables.ModelTable):
id = tables.Column(sortable=False, visible=False)
societe = tables.Column(sortable=False, visible=False)
class Meta:
model = models.get_model('core', "Fournisseur")
I tried this but don't work :
ObjectTable=type('ObjectTable',(tables.ModelTable,),dict(model=myModel))
ObjectTable=type('ObjectTable',(tables.ModelTable,),dict(meta.model=myModel))
ObjectTable=type('ObjectTable',(tables.ModelTable,),dict(meta=myModel))
Thanks.
This is the solution :
def CreateForm(for_model, request=None, instance=None, user=None):
class _StateMachineBaseModelForm(ModelForm):
class Meta:
model = for_model
exclude = ('societe',)
def __init__(self, *args, **kwargs):
super(_StateMachineBaseModelForm, self).__init__(*args, **kwargs)
try:
if user:
self.fields['banque'].queryset = Banque.objects.filter(pays=user.get_profile().societe.pays)
except:
pass
if for_model: return _StateMachineBaseModelForm(request, instance=instance)
It's the exact same thing with the values that you find in your django example. Try it for yourself.
All of your examples "do not work" as you put it, because (a) you don't create any fields other than meta (b) that should be spelled Meta (c) the value of Meta should be an (old style) class.
Before we start, I agree with S. Lott in the comments. You almost certainly don't want to do this.
Here goes:
# You can't create an old-style class with type,
# so define a function that creates one.
def make_meta_class():
class Meta:
model = models.get_model('core', "Fournisseur")
return Meta
# Create the dictionary (remember to include the fields as well as the meta class)
d=dict(,
Meta=make_meta_class(),
id=tables.Column(sortable=False, visible=False)
societe=tables.Column(sortable=False, visible=False)
)
# Create the class dynamically
ObjectTable=type('ObjectTable', (tables.ModelTable,), d)