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)
Related
I am working on a django app that lets the user create a point on a map. This map needs to be centered at a certain location for ease of use, and this location is variable. I am using a LeafletWidget to let the user create this point.
The map can be centered by changing the attributes of the LeafletWidget. Unfortunately this widget is defined inside a Meta class, and as far as I understand you cannot pass a variable into it. Here an example of what I would like to do, somehow passing the variable center into the Meta class. center is a tuple with latitude and longitude values. This example code does not work of course. I don't know if you can somehow pass a variable into a Meta class.
class PointModelForm(ModelForm):
class Meta(center):
model = PointModel
fields = ['name', 'point']
widgets = {'point': LeafletWidget(attrs={'settings_overrides': {'DEFAULT_CENTER': center} })}
My best bet was defining the widgets attribute in the __init__ function, however Meta classes cannot access these attributes if I am correct. When I run this no widgets are used.
class PointModelForm(ModelForm):
class Meta:
model = PointModel
fields = ['name', 'point']
def __init__(self, center):
super(PointModelForm, self).__init__()
self.widgets = {'point': LeafletWidget(attrs={'settings_overrides': {'DEFAULT_CENTER': center} })}
Do you have any idea how to do this? Thanks in advance!
You can specify the attrs of a widget with:
class PointModelForm(ModelForm):
class Meta:
model = PointModel
fields = ['name', 'point']
def __init__(self, *args, center=None, **kwargs):
super(PointModelForm, self).__init__(*args, **kwargs)
if center is not None:
attrs = self.fields['point'].widget.attrs
subattrs = attrs.setdefault('settings_overrides', {})
subattrs['DEFAULT_CENTER'] = center
You can then construct a PointModelForm with:
PointModelForm(center=(55, 12)) # for GET request
PointModelForm(request.POST, request.FILES, center=(55, 12)) # for POST request
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)
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
My problem is a python/django mix. I have a form model that will display some fields. Basing on some parameter of this model, the data sent to metaclass creating this object should differ. But how can I reach this parameter when inside the body of Meta ? Should I use some global var instead of object parameter (as it is introduced only to store value temporarily) ?
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
instance = kwargs.get("instance")
self.type = None
try:
type = self.instance.template_id
except:
pass
class Meta:
model = ContentBase
fields = ["title", "slug", "description", "text", "price",]
#here I need to have the value of 'type'
if type != 2:
try:
fields.remove("price")
except:
pass
You can't do anything dynamic within Meta. That's not what it's for.
Why can't you do it all within __init__? You can modify self.fields from there.
Just as Daniel proposed, I moved the whole thing to __init__ :
type = None
try:
type = self.instance.template_id
except:
pass
if type != 2:
self.fields.pop("price")
else:
I have a problem with serialization of Django inherited models. For example
class Animal(models.Model):
color = models.CharField(max_length=50)
class Dog(Animal):
name = models.CharField(max_length=50)
...
# now I want to serialize Dog model with Animal inherited fields obviously included
print serializers.serialize('xml', Dog.objects.all())
and only Dog model has been serialized.
I can do smth like
all_objects = list(Animal.objects.all()) + list(Dog.objects.all())
print serializers.serialize('xml', all_objects)
But it looks ugly and because my models are very big so I have to use SAX parser and with such output it's difficult to parse.
Any idea how to serialize django models with parent class?
**EDIT: ** It use to work ok before this patch has been applied. And the explanation why the patch exist "Model saving was too aggressive about creating new parent class instances during deserialization. Raw save on a model now skips saving of the parent class. " I think there should be an option to be able to serialize "local fields only" by default and second option - "all" - to serialize all inherited fields.
You found your answer in the documentation of the patch.
all_objects = list(Animal.objects.all()) + list(Dog.objects.all())
print serializers.serialize('xml', all_objects)
However, if you change Animal to be an abstract base class it will work:
class Animal(models.Model):
color = models.CharField(max_length=50)
class Meta:
abstract = True
class Dog(Animal):
name = models.CharField(max_length=50)
This works as of Django 1.0. See http://docs.djangoproject.com/en/dev/topics/db/models/.
You'll need a custom serializer to support inherited fields, as Django's serializer will only serialize local fields.
I ended up writing my own when dealing with this issue, feel free to copy it: https://github.com/zmathew/django-backbone/blob/master/backbone/serializers.py
In order to use it on its own, you need to do:
serializer = AllFieldsSerializer()
serializer.serialize(queryset, fields=fields)
print serializer.getvalue()
I had the same problem, and i wrote a 'small' queryset serializer which navigates up the inheritance tree and returns all the fields serialized.
It's far from perfect... but works for me :)
a = QuerySetSerializer(MyModel, myqueryset)
a.serialize()
And the snippet:
from __future__ import unicode_literals
import json
import inspect
from django.core import serializers
from django.db.models.base import Model as DjangoBaseModel
class QuerySetSerializer(object):
def __init__(self, model, initial_queryset):
"""
#param model: The model of your queryset
#param initial_queryset: The queryset to serialize
"""
self.model = model
self.initial_queryset = initial_queryset
self.inheritance_tree = self._discover_inheritance_tree()
def serialize(self):
list_of_querysets = self._join_inheritance_tree_objects()
merged_querysets = self._zip_queryset_list(list_of_querysets)
result = []
for related_objects in merged_querysets:
result.append(self._serialize_related_objects(related_objects))
return json.dumps(result)
def _serialize_related_objects(self, related_objects):
"""
In this method, we serialize each instance using the django's serializer function as shown in :
See https://docs.djangoproject.com/en/1.10/topics/serialization/#inherited-models
However, it returns a list with mixed objects... Here we join those related objects into one single dict
"""
serialized_objects = []
for related_object in related_objects:
serialized_object = self._serialize_object(related_object)
fields = serialized_object['fields']
fields['pk'] = serialized_object['pk']
serialized_objects.append(fields)
merged_related_objects = {k: v for d in serialized_objects for k, v in d.items()}
return merged_related_objects
def _serialize_object(self, obj):
data = serializers.serialize('json', [obj, ])
struct = json.loads(data)
return struct[0]
def _discover_inheritance_tree(self):
# We need to find the inheritance tree which excludes abstract classes,
# so we can then join them when serializing the instance
return [x for x in inspect.getmro(self.model) if x is not object and x is not DjangoBaseModel and not x._meta.abstract]
def _join_inheritance_tree_objects(self):
"""
Here we join the required querysets from the non abstract inherited models, which we need so we are able to
serialize them.
Lets say that MyUser inherits from Customer and customer inherits from django's User model
This will return [list(MyUser.objects.filter(...), list(Customer.objects.filter(...), list(User.objects.filter(...)
"""
initial_ids = self._get_initial_ids()
inheritance__querysets = [list(x.objects.filter(id__in=initial_ids).order_by("id")) for x in self.inheritance_tree]
return inheritance__querysets
def _zip_queryset_list(self, list_of_querysets):
"""
At this stage, we have something like:
(
[MyUser1, MyUser2, MyUser3],
[Customer1, Customer2, Customer3],
[User1, User2, User3]
)
And to make it easier to work with, we 'zip' the list of lists so it looks like:
(
[MyUser1, Customer1, User1],
[MyUser2, Customer2, User2],
[MyUser3, Customer3, User3],
)
"""
return zip(*list_of_querysets)
def _get_initial_ids(self):
"""
Returns a list of ids of the initial queryset
"""
return self.initial_queryset.order_by("id").values_list("id", flat=True)
You can define a custom Serializer:
class DogSerializer(serializers.ModelSerializer):
class Meta:
model = Dog
fields = ('color','name')
Use it like:
serializer = DogSerializer(Dog.objects.all(), many=True)
print serializer.data enter code here
Did you look at select_related() ?
as in
serializers.serialize('xml', Dog.objects.select_related().all())