create Django form class with dynamic number of CharFields - python

I'm having problem of creating a dynamic class form that contains a list of CharFields.
I'm able to create a normal class with dynamic keys values as below. but when I change the values to be a CharFields. it doesn't work. What's wrong with the form?
=========Normal class===========
class DynamicClass(forms.Form):
def __init__ (self, data):
self.data =data
for key in self.data.keys():
self.__setattr__(key, self.data[key])
my_class = DynamicClass({"HOSTNAME": 'Router_Hostname: ', "IP": 'IP_ADDRESS:'})
print (my_class.HOSTNAME)
output --> Router_Hostname:
print (my_class.IP)
output--> IP_ADDRESS:
=========Form class===========
class DynamicForm(forms.Form):
def __init__ (self, data):
self.data =data
for key in self.data.keys():
self.__setattr__(key, forms.CharField(label = self.data[key]))
my_form = DynamicForm ({"HOSTNAME": 'Router_Hostname: ', "IP": 'IP_ADDRESS:'})
print (my_form.as_table)
error output-->
Traceback (most recent call last):
File "", line 1, in
File "~/automation/lib/python3.8/site-packages/django/forms/forms.py", line 137, in repr
if self._errors is None:
AttributeError: 'DynamicForm' object has no attribute '_errors'

I got it solved
firstly, i shouldn't use "label" to set initial value for CharField(). Instead, use 'initial' keyword.
2nd, missing super(DynamicForm, self).__init__(*args, **kwargs)
finally a working solution:
class DynamicForm(forms.Form):
def __init__ (self, data, *args, **kwargs):
super(DynamicForm, self).__init__(*args, **kwargs)
self.data = data
for key in self.data.keys():
self.fields[key] = forms.CharField(initial = self.data[key])

Related

Mock Patch check parent class method call

I want to unit test this class method update
class EmployeeUpdateSerializer(serializers.ModelSerializer):
def update(self, instance, data):
shift_types = data.pop('shift_types', None)
instance = super().update(instance, data)
self.update_shift_type(instance, shift_types)
return instance
I am doing this
class TestEmployeeUpdateSerializer(TestCase):
def setUp(self):
self.company, self.user, self.header = create_user_session()
self.serializer = EmployeeUpdateSerializer()
def test_update(self):
employee = self.user
with patch.object(self.serializer, 'update') as update:
with patch.object(self.serializer, 'update_shift_type') as update_shift_type:
res = self.serializer.update(employee, dict())
update.assert_called_once_with(employee, dict())
update_shift_type.assert_called_once_with(employee, None)
self.assertEqual(res, employee)
But this gives me an error
Traceback (most recent call last):
File "/Users/shahzadfarukh/my-croft/backend/account/tests/test_employee_serializers.py", line 222, in test_update
update_shift_type.assert_called_once_with(employee, None)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py", line 830, in assert_called_once_with
raise AssertionError(msg)
AssertionError: Expected 'update_shift_type' to be called once. Called 0 times.
Please help me! is something I am doing wrong?
You have mocked update, so its original code won't get called. If you want to test what update calls, you have to call the original version, and mock only functions/methods called within the update. Assuming the base class is imported like import serializers, you could do something like this (untested)
class TestEmployeeUpdateSerializer(TestCase):
def setUp(self):
self.company, self.user, self.header = create_user_session()
self.serializer = EmployeeUpdateSerializer()
#patch('serializers.ModelSerializer.update')
#patch('serializers.ModelSerializer.update_shift_type')
def test_update(self, mocked_update_shift_type, mocked_update):
employee = self.user
res = self.serializer.update(employee, dict())
mocked_update.assert_called_once_with(employee, dict())
mocked_update_shift_type.assert_called_once_with(employee, None)
self.assertEqual(res, employee)

Question about multiple inheritance, dynamic class creating and instantiation

Hello I have the following situation:
A specialized class that inherits from two parent class
The need to define the most specialized class at run time, based on some information that I get only when I start reading data from a database.
I defined the following code to handle the create all the classes in the chain:
class BusinessDocument():
#staticmethod
def get_class(doc_type):
switch = {
'MasterData': MasterData,
'Transactional': Transactional
}
func = switch.get(doc_type, lambda: "Invalid Noun Type")
return func()
def __init__(self, doc_id, location, doc_type):
self.doc_id = doc_id
self.location = location
self.doc_type = doc_type
pass
#property
def get_location(self):
return self.location
#property
def get_doc_id(self):
return self.doc_id
class MasterData(BusinessDocument):
def __init__(self, doc_id, location):
BusinessDocument.__init__(self, doc_id, location, 'MasterData')
class Transactional(BusinessDocument):
def __init__(self, doc_id, location):
BusinessDocument.__init__(self, doc_id, location, 'Transactional')
class NounClass():
#staticmethod
def get_class(doc_name, doc_type):
return type(doc_name, (BusinessDocument.get_class(doc_type),
BusinessDocument, ),dict.fromkeys(['doc_id', 'location']))
Then at run time when I get the doc_name and I try to create a new class. At this point I may not have the required arguments doc_id and location but I need to class type.
invoice_cls = NounClass.get_class('Invoice', 'Transactional')
I get the following error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-10-cb774746875a> in <module>
----> 1 invoice_cls = NounClass.get_class('Invoice', 'Transactional')
<ipython-input-9-aa5e0b316ed1> in get_class(doc_name, doc_type)
35 #staticmethod
36 def get_class(doc_name, doc_type):
---> 37 return type(doc_name, (BusinessDocument.get_class(doc_type),
38 BusinessDocument, ),dict.fromkeys(['doc_id', 'location']))
<ipython-input-9-aa5e0b316ed1> in get_class(doc_type)
7 }
8 func = switch.get(doc_type, lambda: "Invalid Noun Type")
----> 9 return func()
10
11 def __init__(self, doc_id, location, doc_type):
TypeError: __init__() missing 2 required positional arguments: 'doc_id' and 'location'
I understand that the reason for it is because the __init__() will be called during the class instantiation, but I thought that type would be only creating a new type and not instantiate one right away. So my question is if is there a way to defer the instantiation of the instance at this time.
Thank you in advance for any help and tips on this.
--MD.
The initalization occurs on line 9:
return func()
I assume you want to return a class object, so remove those parantheses.
Also func is misleding, I've changed it to cls:
def get_class(doc_type):
switch = {
'MasterData': MasterData,
'Transactional': Transactional
}
cls = switch.get(doc_type, lambda: "Invalid Noun Type")
return cls

How to pass arguments to a Serializer in django-rest-framework?

I have a serializer as:
class DataSerializer(serializers.Serializer):
skip_place = True
name = serializers.CharField(max_length=None)
place = serializers.CharField(max_length=None,required=False)
def validate_place(self,value):
if not skip_place and len(value)<=0:
raise serializers.ValidationError("Place is required.")
Now observe skip_place. If I call the DataSerializer instance as:
DataSerializer(data=data, skip_place=False)
Then it should validate over place as in the code.
But I was not able to pass the argument skip_place=True. I get an error: TypeError: __init__() got an unexpected keyword argument 'skip_place'
You can send it with including extra context.
In view;
DataSerializer(data=data, context={'skip_place': True}) # or False
In serializer;
class DataSerializer(serializers.Serializer):
name = serializers.CharField(max_length=None)
place = serializers.CharField(max_length=None,required=False)
def validate_place(self,value):
skip_place = self.context.get("skip_place") # <- here we use self.context to get extra args
if not skip_place and len(value)<=0:
raise serializers.ValidationError("Place is required.")
Hi You can override the init method of serializer, pop the value from there and assign into variable.
class DataSerializer(serializers.Serializer):
skip_place = True
name = serializers.CharField(max_length=None)
place = serializers.CharField(max_length=None,required=False)
def __init__(self, *args, **kwargs):
self.skip_place = kwargs.pop('skip_place ', False)
super().__init__(*args, **kwargs)
def validate_place(self,value):
if not self.skip_place and len(value)<=0:
raise serializers.ValidationError("Place is required.")

No attribute error even when the attribute is set

So I have a class that extends two classes deep, here is it's definition and __init__():
class ProspectEventSocketProtocol(ChannelEventSocketProtocol):
def __init__(self, *args, **kwargs):
super(ProspectEventSocketProtocol, self).__init__(*args, **kwargs)
self.channel_info = None
self.rep_uuid = None
self.manual_dial = None
self.datetime_setup = timezone.now()
self.datetime_answered = None
self.defer_until_answered = defer.Deferred()
self.defer_until_originated = defer.Deferred()
self.defer_until_finished = defer.Deferred()
The definition and __init__() for the ChannelEventSocketProtocol is here:
class ChannelEventSocketProtocol(Freeswitch):
def __init__(self, *args, **kwargs):
self.channel_driver = None
self.uuid = kwargs.pop('uuid', str(uuid4()))
self._call_driver = kwargs.pop('call_driver', None)
super(ChannelEventSocketProtocol, self).__init__(*args, **kwargs)
And the definition and __init__() for the Freeswitch class is here:
class Freeswitch(client.EventSocketProtocol, TwistedLoggingMixin):
def __init__(self, *args, **kwargs):
self.jobs = {}
self.defer_until_authenticated = defer.Deferred() # This is the problem
client.EventSocketProtocol.__init__(self, *args, **kwargs)
TwistedLoggingMixin.__init__(self)
Even though I know that this is running and the defer_until_authenticated is being set as well as it's callback and errback, when I call this:
live_call = yield self._create_client_dial_live_call(client_dial.cid, client_dial.campaign)
pchannel = yield self.realm.get_or_create_channel_driver(live_call.uuid, 'prospect')
# ...
client_dial.prospect_channel = pchannel
yield pchannel.freeswitch_protocol.defer_until_authenticated # This is the problem here!
I get the error:
type object 'ProspectEventSocketProtocol' has no attribute 'defer_until_authenticated'
I have no idea why I can't get the attribute again. I know it is being set, but I have no idea where it goes... or what happens to it. I've searched the error and I have no idea what is happening in this spot.
Just for reference, here are the _create_client_dial_live_call() and get_or_create_channel_driver() functions:
def _create_client_dial_live_call():
# ...
p, created = Prospect.objects.get_or_create_client_dial_prospect(campaign, cid_num)
# ...
live_call = LiveCall(prospect=p, campaign=campaign.slug)
live_call.channel_vars_dict = chan_vars
live_call.save()
# ...
def get_or_create_channel_driver()
# The code is kind of confusing with even more context,
# it basically either gets the existing ProspectChannel
# object or creates a new one and then returns it.
Something somewhere is forgetting to instantiate a class.
The error is not telling you that an instance of the class ProspectEventSocketProtocol has no attribute defer_until_authenticated. It's telling you that the class ProspectEventSocketProtocol itself has no such attribute.
In other words, you are quite probably writing something like
pchannel.freeswitch_protocol = ProspectEventSocketProtocol
when you want
pchannel.freeswitch_protocol = ProspectEventSocketProtocol(...)
instead.
Here's a quick demo script that reproduces the error message you are seeing:
#!/usr/bin/env python3
class Test(object):
def __init__(self):
self.arg = "1234"
correct = Test()
print(correct.arg)
wrong = Test
print(wrong.arg)
When I run it, I get the following output:
1234
Traceback (most recent call last):
File "./type_object_error.py", line 12, in <module>
print(wrong.arg)
AttributeError: type object 'Test' has no attribute 'arg'

Overriding decorated subclass methods

I'm fiddling around with inheritance and found a behavior that seems strange to me---namely, that some times I can override a parent decorator function (used for validation), but sometimes I cannot, and I cannot understand why or what the difference is.
A quick walkthrough in words---I have a person object I'd like subclass to a more particular person object. The more particular one will have an additional field, "Dance," and will have different validation rules on a previous field, "name."
Here's my base case which works:
# Define the validation wrapper
def ensure(name, validate, doc=None):
def decorator(Class):
privateName = "__" + name
def getter(self):
return getattr(self, privateName)
def setter(self, value):
validate(name, value)
setattr(self, privateName, value)
setattr(Class, name, property(getter, setter, doc=doc))
return Class
return decorator
# Define the not string validation
def is_not_str(name, value):
if isinstance(value, str):
raise ValueError("{} cannot be a string.".format(name))
# Chosen to be exact opposite of above---demonstrating it's possible to reverse.
def is_str(name, value):
if not isinstance(value, str):
raise ValueError("{} must be a string.".format(name))
#ensure("name", is_str)
#ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = s.get('name',{})
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
#ensure("name", is_not_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s) # idiom to inherit init
self.dance = s.get('dance') # add new param.
bill = Person({"name":"bill",
"url":"http://www.example.com"})
fred = Crazyperson({"name":1,
"url":"http://www.example.com",
"dance":"Flamenco"})
This works fine. So, the first object, bill, is created in such a way that the validation is_str succeeds. If you try to put a number there, it fails. The second object, likewise, accepts non-strings, so fred is created successfully.
Now, here's the case where it breaks, which I'd like to understand...
def is_Name(name, value):
if not isinstance(value, dict) and not isinstance(value,Name):
raise ValueError("{} must be a valid Name object".format(name))
# new object that will be a non-string type of name.
#ensure("firstname", is_str)
#ensure("lastname", is_str)
class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')
def __str__(self):
return "Name({{'firstname':'{}','lastname':'{}' }})".format(self.firstname, self.lastname)
def __repr__(self):
return str(self)
#ensure("name", is_Name) # require it as the default for the base class
#ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
#ensure("name", is_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.name = s.get('name','') # THIS IS THE KEY
self.dance = s.get('dance')
bill = Person({"name":{"firstname":"Bill", "lastname":"billbertson"},
"url":"http://www.example.com"})
fred = Crazyperson({"name":"Fred",
"url":"http://www.example.com",
"dance":"Flamenco"})
In this instance, the Crazyperson fails. The error suggests that the is_Name validation function in the __init__ is still being applied:
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "<stdin>", line 4, in __init__
File "<stdin>", line 5, in __init__
File "<stdin>", line 5, in __init__
AttributeError: 'str' object has no attribute 'get'
It looks like it has called the Name initializer: Name(s.get('name',{})) on the string name "Fred".
But it seems it can't be, because in the previous example, I was able to remove a completely contradictory validation (is_str versus is_not_str). Why is this less opposite but failing more? In the first case it wasn't applying both is_str and is_not_str, why is it /now/ applying both is_Name and is_str with seemingly identical syntax?
My question is: what's different about the first way of doing this that causes it to succeed from the second way? I've tried to isolate variables here, but don't understand why I can undo the wrapped validator inherited from the parent class in Scenario I but cannot do what seems similar in Scenario II. It seems the only meaningful difference is that it's an object instead of a string.
(I understand that the better architectural way to do this would be to have a third more abstract parent class, with no validation rules that need changing---and both kinds of person would inherit from that. But I also understand I am supposed to be able to change methods in subclasses, so I'd like to at least understand the difference between why one is succeeding and the other failing here.)
In your second setup, the is_Name function is not applied. You are creating Name object, regardless, in the __init__ method:
class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')
Note the self.name = Name(...) line there.
In Crazyperson.__init__() you call the parent method:
def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.dance = s.get('dance')
passing on s to Person.__init__() which creates a Name() object.
So when you create fred with fred = Crazyperson({"name":"Fred", ...}) you are passing name set to the string 'Fred' to Name.__init__(), which expected a dictionary instead:
class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')
and this is where your code fails:
>>> 'Fred'.get('firstname', '')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'get'
Only set name on Person if no self.name has been set yet:
class Person(object):
def __init__(self,s):
if not hasattr(self, 'name')
self.name = Name(s.get('name', {}))
self.url = s.get('url','')
and set name first in Crazyperson:
def __init__(self,s):
self.name = s.get('name', 0)
self.dance = s.get('dance')
super(Crazyperson,self).__init__(s)

Categories