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:
Related
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 have a class which takes only kwargs. The argument kwargs['content'] contains a user and theme keys:
> content = "{'user': 1, 'theme':'red'}"
> kwargs['content'] = content
> m = Message(**kwargs)
I need a flexible way to model what is in kwargs['content'] and check...
some keys in kwargs['content'] are always present.
always make sure the values are not null and match the type.
This is what I have tried, but I feel like there is a better way.
class Message(object):
def __init__(self, *args, **kwargs):
for field in self._hash_model():
if field not in kwargs['content']:
raise ValidationError('Missing % field'.format(field))
# check type
# turn to json
def _hash_model(self):
"""
My model of values that need to be in content
"""
model = {
'user': int,
'theme': str
}
return model
I'm using Python 3.4.
See if the following works for you. Change the __init__ to
def __init__(self, *args, **kwargs):
content = json.loads(kwargs["content"]) # assuming kwargs has "content" key
for key,val in self._hash_model().items():
field = content.get(key) # returns None if absent
if field and typeof(field) is val:
# go ahead
else:
raise SomeException
Factor out the validation logic to a method if you see fit. Comment if it does not work.
What I need is returning all the parameters from field :
Example :
Class MyClass(models.Model):
field = models.Charfield(blank = True, null = True)
I want to return all the parameters of "field" from "MyClass".
Here, it should be the parameters blank and null.
For a model MyModel
class MyModel(models.Model):
my_field = models.Charfield(max_length=100)
You can get the field using the Meta api.
>>> field = MyModel._meta.get_field('my_field')
You can then use the deconstruct method to get the kwargs that were passed to the field when it was instantiated.
>>> name, path, args, kwargs = field.deconstruct()
>>> print(kwargs)
{u'max_length': 100}
I did not really get the purpose of getting the parameters, but I think you can use 'vars'. It's kind of wrap for '__dict__'.
As django model fields have '__dict__' attribute, 'vars' will give you access to the field's parameters as follow:
def has_parameter(field_name, parameter_name):
parameter_dict = vars(field_name)
return not parameter_dict.get(parameter_name) == None
if equal to None means it's not set yet otherwise it's set or has a default value.
in you case: >>> has_parameter(field_name,'through')
First of all: I am not able to find out the proper Title of this question.
Anyhow the question is:
I have to fill a form at template and the fields of this form are user dependent. For example you passes integer (integer is not a datatype) as a parameter to the method and it should returns like this:
fileds = forms.IntegerField()
If you pass bool then it should like this:
fields = forms.BooleanField()
So that i can use them to create my form. I tried with this code but it returns into the form of string.
Some.py file:
choices = (('bool','BooleanField()'),
('integer','IntegerField()'))
def choose_field():
option = 'bool' # Here it is hardcoded but in my app it comes from database.
for x in choices:
if x[0]==option:
type = x[1]
a = 'forms'
field = [a,type]
field = ".".join(field)
return field
When i print the field it prints 'forms.BooleanField()'. I also use this return value but it didn't work. Amy solution to this problem?
The simpliest way is to create your form class and include fields for all possible choices to it. Then write a constructor in this class and hide the fields you don't want to appear. The constructor must take a parameter indicating which fields do we need. It can be useful to store this parameter in the form and use it in clean method to correct collected data accordingly to this parameter.
class Your_form(forms.ModelForm):
field_integer = forms.IntegerField()
field_boolean = forms.BooleanField()
def __init__(self, *args, **kwargs):
option = kwargs["option"]
if option == "integer":
field_boolean.widget = field_boolean.hidden_widget()
else:
field_integer.widget = field_integer.hidden_widget()
super(Your_form, self).__init__(*args, **kwargs)
In your controller:
option = 'bool'
form = Your_form(option=option)
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)