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

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.")

Related

How to update/change attribute in Django Class Based View Test

I created a mixin that requires an attribute to be added to a Class Based View (cbv) and it works as expected - but I'm having a tough time 'testing' that mixin specifically.
Here is my test:
class TestView(GroupRequiredMixin, View):
group_required = ("test_group",)
raise_exception = True
def get(self, request):
return HttpResponse("OK")
class GroupRequiredMixinTest(TestCase):
def setUp(self):
group = GroupFactory(name="test_group")
self.user = UserFactory(groups=(group,))
def test_view_without_group_required_improperly_configured(self):
rfactory = RequestFactory()
request = rfactory.get("/fake-path")
request.user = self.user
view = TestView()
view.group_required = None
with self.assertRaises(ImproperlyConfigured):
view.as_view()(request)
This test errors with this message: AttributeError: This method is available only on the class, not on instances.
What is the 'appropriate' way to test this?
Should I be setting up a unique class for each situation I want to test? Or is there a way I can dynamically change that attribute on an instance like in my test that fails?
*Edit to add the mixin (almost identical to the core Django version for Permissions):
class GroupRequiredMixin(AccessMixin):
"""Verify that the current user has all specified groups."""
group_required = None
def handle_no_group(self):
if self.raise_exception or self.request.user.is_authenticated:
raise PermissionDenied(self.get_permission_denied_message())
path = self.request.build_absolute_uri()
resolved_login_url = resolve_url(self.get_login_url())
# If the login url is the same scheme and net location then use the
# path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if (not login_scheme or login_scheme == current_scheme) and (
not login_netloc or login_netloc == current_netloc
):
path = self.request.get_full_path()
return redirect_to_login(
path,
resolved_login_url,
self.get_redirect_field_name(),
)
def get_group_required(self):
"""
Override this method to override the group_required attribute.
Must return an iterable.
"""
if self.group_required is None:
raise ImproperlyConfigured(
f"{self.__class__.__name__} is missing the "
f"group_required attribute. Define "
f"{self.__class__.__name__}.group_required, or override "
f"{self.__class__.__name__}.get_group_required()."
)
if isinstance(self.group_required, str):
groups = (self.group_required,)
else:
groups = self.group_required
return groups
def has_group(self):
"""
Override this method to customize the way groups are checked.
"""
groups = self.get_group_required()
if hasattr(self.request.user, "has_groups"):
return self.request.user.has_groups(groups)
else:
return self.handle_no_group()
def dispatch(self, request, *args, **kwargs):
if not self.has_group():
return self.handle_no_group()
return super().dispatch(request, *args, **kwargs)
with self.assertRaises(ImproperlyConfigured):
> view.as_view()(request)
my_app/tests/test_mixins.py:52:
_ _ _ _ _
def __get__(self, instance, cls=None):
if instance is not None:
> raise AttributeError("This method is available only on the class, not on instances.")
E AttributeError: This method is available only on the class, not on instances.
I was able to get it working with the following:
rfactory = RequestFactory()
request = rfactory.get("/fake-path")
request.user = self.user
view = TestView.as_view(group_required=None)
with self.assertRaises(ImproperlyConfigured):
view(request)
I could not call TestView() to instantiate a new view - it needed to be TestView.as_view() to instantiate it. Then the arguments get passed inside the as_view() portion.
Working as intended now.

How to override mongoengine's QuerySet method?

How can I override a mongoengine's queryset method?
Specifically, I want to override .order_by(), but the closest I can get is to add another method ordered that would conditionally call .order_by():
class TransactionQuerySet(QuerySet):
def ordered(self, *args, target_data=None, **kwargs):
if target_data is None:
result = self.order_by(*args, **kwargs)
return result
else:
return 'transactions sorted by target data'
Ideally I would like this new method to be named the same as mongoengine's method - order_by - but if I do this I will exceed recursion depth when the queryset manager is called like Transaction.objects.order_by.
How can I do this?
To avoid recursion, you should call order_by method of the parent class. TransactionQuerySet should look like the following.
class TransactionQuerySet(QuerySet):
def order_by(self, *args, target_data=None):
if target_data is None:
return super().order_by(*args)
return "transactions sorted by target data"
Now if you call order_by on TransactionQuerySet object it won't fall in recursion.
class Transaction(Document):
title = StringField(max_length=100, required=True)
last_modified = DateTimeField(default=datetime.utcnow)
def __str__(self):
return self.title
meta = {"collection": "transaction_collection"}
with MongoClient("mongodb://localhost:27017") as client:
connection = client.get_database("transaction")
collection = connection.transaction_collection
transaction_set = TransactionQuerySet(Transaction, collection)
print(transaction_set.order_by("-title"))
OUTPUT
[<Transaction: ETHUSTD>, <Transaction: BTCUSTD>, ...]

python template with default value

In python I can use a template with
from string import Template
templ = Template('hello ${name}')
print templ.substitute(name='world')
how can I define a default value in the template?
And call the template without any value.
print templ.substitute()
Edit
And when I call without parameters get the default value, example
print templ.substitute()
>> hello name
The Template.substitute method takes a mapping argument in addition to keyword arguments. The keyword arguments override the arguments provided by the mapping positional argument, which makes mapping a natural way to implement defaults without needing to subclass:
from string import Template
defaults = { "name": "default" }
templ = Template('hello ${name}')
print templ.substitute(defaults) # prints hello default
print templ.substitute(defaults, name="world") # prints hello world
This will also work for safe_substitute:
print templ.safe_substitute() # prints hello ${name}
print templ.safe_substitute(defaults) # prints hello default
print templ.safe_substitute(defaults, name="world") # prints hello world
If you are absolutely insistent on passing no arguments to substitute you could subclass Template:
class DefaultTemplate(Template):
def __init__(self, template, default):
self.default = default
super(DefaultTemplate, self).__init__(template)
def mapping(self, mapping):
default_mapping = self.default.copy()
default_mapping.update(mapping)
return default_mapping
def substitute(self, mapping=None, **kws):
return super(DefaultTemplate, self).substitute(self.mapping(mapping or {}), **kws)
def substitute(self, mapping=None, **kws):
return super(DefaultTemplate, self).safe_substitute(self.mapping(mapping or {}), **kws)
And then use it like this:
DefaultTemplate({ "name": "default" }).substitute()
Although I find this to be less explicit and less readable than just passing a mapping with defaults to substitute.
You can create your proxy to Template class and store substitutes there.
from string import Template
from copy import copy
class TemplateWithDefaults(Template):
def __init__(self, template, **defaults):
self.defaults = defaults or {}
super(TemplateWithDefaults, self).__init__(template)
def build_mapping(self, *args, **kwargs):
mapping = copy(self.defaults)
if len(args) == 1:
mapping.update(args[0])
mapping.update(kwargs)
return mapping
def substitute(*args, **kwargs):
self, args = args[0], args[1:]
mapping = self.build_mapping(*args, **kwargs)
return super(TemplateWithDefaults, self).substitute(mapping, **kwargs)
def safe_substitute(*args, **kwargs):
self, args = args[0], args[1:]
mapping = self.build_mapping(*args, **kwargs)
return super(TemplateWithDefaults, self).safe_substitute(mapping, **kwargs)
template = TemplateWithDefaults("$wow", wow=1)
print template.substitute() # outputs 1
print template.substitute(wow=2) # outputs 2
print template.substitute({"wow": 2}) # outputs 2
print template.substitute() # outputs 1 (means no side effects)
UPD: edited code to handle dict as first argument. Original api compatibility.
If the default value is the variable name (like in the question), the missing data could be added automatically:
class MyTemplate(Template):
def substitute(self, *args, **kwds):
try:
return super().substitute(*args, **kwds)
except KeyError as err:
key = str(err.args[0])
kwds[key] = key
return self.substitute(*args, **kwds)

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'

Python Decorated Class does not allow method calls. Why?

As i mentioned in this previous post. Im trying to create a decorator which does the following:
The decorated class represents a document in a Documentbased DB like CouchDB or MongoDB. The Decorator accepts on argument which is a instance of a connector to such a database. The Model Class (in this example User) does automatically map undefined attributes to fields in the DB.
Now i get stuck a bit :-/ The mentioned things is all working. But now i cant call any methods from the Model Class. I'm getting the following error.
TypeError: unbound method myfunc() must be called with User instance
as first argument (got nothing instead)
class Connector(object):
def readvar(self, var):
data = {"emailAddress":"jack.bauer#ctu.org", "lastName":"Bauer"}
return data[var]
class DocumentDB(object):
def __init__(self,connector):
self.connector = connector
def __call__(self, *args, **kargs):
_c = self.connector
class TransparentAttribute:
def __getattr__(self, attrname):
try:
return _c.readvar(attrname)
except:
return getattr(args[0], attrname)
return TransparentAttribute
c = Connector()
#DocumentDB(c)
class User(object):
username = "JackBauer"
def doSomething(self):
print "bla bla"
def doSomethingElse(self):
pass
def myfunc(self):
print "afadsadsf adsf asdf asdf"
u = User()
u.myfunc() # Does not work!!!
print u.emailAddress
print u.lastName
print u.username
I've had a quick play, and it looks like subclassing the user works.
class DocumentDB(object):
def __init__(self,connector):
self.connector = connector
def __call__(self, user):
_c = self.connector
print self, user, _c # <__main__.DocumentDB object at 0x012DAD30> <class '__main__.User'> <__main__.Connector object at 0x012DAD70>
class TransparentAttribute(user):
def __getattr__(self, attrname):
try:
return _c.readvar(attrname)
except:
return getattr(user, attrname)
return TransparentAttribute
u = User()
print type(u) # <class '__main__.TransparentAttribute'>
u.myfunc() # afadsadsf adsf asdf asdf
After u = User(), u is of type TransparentAttribute, and I think if you don't subclass it then you basically replace your User instance with a TransparentAttribute instance (so all the User object's local functions are gone).
(But to be honest, some of this is a bit over my head - feel free to correct me)
args[0] is the User class object, not an instance as you except, hence you get an unbound method (aka class method) and not a bound method.
#DocumentDB(c)
class User(object):
pass
can be rewritten as
class User(object):
pass
User = DocumentDB(c)(User)
which makes the problem more clear (btw, does TransparentAttribute deliberately not inherit from object?)
Maybe you can get what you want not with a decorator but by using Connector as an additional base class to User?

Categories