Custom date widget in django? - python

In my current application, which is based on python and django, I created a custom widget for date.
from datetime import date
from django.forms import widgets
class DateSelectorWidget(widgets.MultiWidget):
def __init__(self, attrs=None):
# create choices for months, years
# example below, the rest snipped for brevity.
years = [(year, year) for year in range(1945, 2016)]
months = [(1,'Jan'),(2,'Feb')]
_widgets = (
widgets.Select(attrs=attrs, choices=months),
widgets.Select(attrs=attrs, choices=years),
)
super(DateSelectorWidget, self).__init__(_widgets, attrs)
def decompress(self, value):
if value:
return [value.month, value.year]
return [None, None]
def format_output(self, rendered_widgets):
return u''.join(rendered_widgets)
def value_from_datadict(self, data, files, name):
datelist = [
widget.value_from_datadict(data, files, name + '_%s' % i)
for i, widget in enumerate(self.widgets)]
try:
D = date(day=1, month=int(datelist[0]),
year=int(datelist[1]))
except ValueError:
return ''
else:
return str(D)
It's working fine when form is being loaded (return date object), but when I submit the form and left some of fields as empty in the form then I am getting the following error.
Caught AttributeError while rendering: 'str' object has no attribute 'month'
Request Method: POST
Request URL:
Django Version: 1.3.1
Exception Type: TemplateSyntaxError
Exception Value:
Caught AttributeError while rendering: 'str' object has no attribute 'month'
Exception Location: /var/www/stacks/django-apps/kkk/apps/oooomonth_year.py in decompress, line 21

You are getting the error here,
def decompress(self, value):
if value:
return [value.month, value.year]
return [None, None]
value object has no attribute month. if you want to know the value attributes print dir(value). In this value is a string.
Update:
check the attribute hasattr(value, 'month'). I hope this helps you.

Its came into existence when post data bind into form. (Not stored into DB due validation message or any other issue). since
def decompress(self, value):
if value:
return [value.month, value.year]
return [None, None]
above method is handling date only when date is storing into DB. but when form is not submitted successfully then its behaves like "Str object". so you need to update above method by below defined method:
def decompress(self, value):
if value:
import types
try:
if type(value) == types.StringType:
from datetime import datetime as dt
tmp_date = dt.strptime(value, '%Y-%m-%d')
return [tmp_date.month, tmp_date.year]
else:
return [value.month, value.year]
except:
raise
else:
return [None, None]
Now this method will handle both case either form submitted or not.

Related

WTForms DateTimeField returning True not matter input

I am struggling to understand why WTForms always validates my input even when it doesn't match the default input format stated in the docs. The docs say the default format is
class wtforms.fields.DateTimeField(default field arguments, format='%Y-%m-%d %H:%M:%S')
but my code always returned true
from wtforms import Form, validators
from wtforms.fields import DateTimeField
class InputForm(Form):
timestamp = DateTimeField('TimeStamp', validators=[validators.DataRequired()])
form = InputForm(timestamp='lmao')
form.validate()
# True
Can someone explain this behavior to me?
To put it simply, whether the value you pass to the field can be coerced to a datetime isn't being checked as the validator that you have supplied only checks for existence of the data, not the type.
The DateTimeField does ensure that a value can be coerced to a datetime, but only if the value comes from a form. As you can see below, passing a value in to the form constructor via kwargs (timestamp='lmao') isn't passed through the same test.
Looking at the definition of the DateTimeField, the only method that has custom handling to do with the field being a datetime field is the process_formdata() method (also _value() but that is used by widgets to render fields):
class DateTimeField(Field):
"""
A text field which stores a `datetime.datetime` matching a format.
"""
widget = widgets.TextInput()
def __init__(
self, label=None, validators=None, format="%Y-%m-%d %H:%M:%S", **kwargs
):
super(DateTimeField, self).__init__(label, validators, **kwargs)
self.format = format
def _value(self):
if self.raw_data:
return " ".join(self.raw_data)
else:
return self.data and self.data.strftime(self.format) or ""
def process_formdata(self, valuelist):
if valuelist:
date_str = " ".join(valuelist)
try:
self.data = datetime.datetime.strptime(date_str, self.format)
except ValueError:
self.data = None
raise ValueError(self.gettext("Not a valid datetime value"))
When you instantiate a Form object, the form's process() method is called which calls the process() method of each of the forms fields and passes the formdata (which in this case is None) and a value for the field if one can be found (in this case, 'lmao').
As you can see, there is no process() method defined on the DateTimeField definition above, so it calls Field.process():
def process(self, formdata, data=unset_value):
"""
Process incoming data, calling process_data, process_formdata as needed,
and run filters.
If `data` is not provided, process_data will be called on the field's
default.
Field subclasses usually won't override this, instead overriding the
process_formdata and process_data methods. Only override this for
special advanced processing, such as when a field encapsulates many
inputs.
"""
self.process_errors = []
if data is unset_value:
try:
data = self.default()
except TypeError:
data = self.default
self.object_data = data
try:
self.process_data(data)
except ValueError as e:
self.process_errors.append(e.args[0])
if formdata is not None:
if self.name in formdata:
self.raw_data = formdata.getlist(self.name)
else:
self.raw_data = []
try:
self.process_formdata(self.raw_data)
except ValueError as e:
self.process_errors.append(e.args[0])
try:
for filter in self.filters:
self.data = filter(self.data)
except ValueError as e:
self.process_errors.append(e.args[0])
In this method, the parameter data is 'lmao', and formdata is None. You can see that the call to process_formdata() is guarded by the if formdata is not None: conditional, and so the custom handling for the field defined on DateTimeField is only run on data that comes from a form.
from werkzeug import MultiDict
form = InputForm(formdata=MultiDict([("timestamp", "lmao")]))
print(form.timestamp.raw_data) # ['lmao']
print(form.timestamp.process_errors) # ['Not a valid datetime value']
print(form.validate()) # False
This means that you are responsible for the validity of values that you pass in to your form fields through kwargs to the form constructor.

python udf error in pig

I am trying to run below python udf in Pig
#outputSchema("word:chararray")
def get(s):
out = s.lower()
return out;
I am getting below error :
File "/home/test.py", line 3, in get
out = s.lower()
AttributeError: 'NoneType' object has no attribute 'lower'
You should handle the case when s is none. In most of the examples such as:
from pig_util import outputSchema
#outputSchema('decade:chararray')
def decade(year):
"""
Get the decade, given a year.
e.g. for 1998 -> '1990s'
"""
try:
base_decade_year = int(year) - (int(year) % 10)
decade_str = '%ss' % base_decade_year
print 'input year: %s, decade: %s' % (year, decade_str)
return decade_str
except ValueError:
return None
You need to handle the case when the value is None. So, one possible fix would be to try:
#outputSchema("word:chararray")
def get(s):
if s is None:
return None
return str(s).lower()

method reverse django error

I'm calling a method using reverse but I have problem argument that I am not able to understand.
My error :
Reverse for 'shopping.views.payment_confirmation' with arguments '(35,)' and keyword arguments '{}' not found.
My url:
url(r'^payment_confirmation/(?P<id>\d+\d{2\})/$', 'payment_confirmation', name='payment_confirmation'),
My view:
def show(request):
...
...
payment.submit(settings.SITE_URL + reverse("shopping.views.payment_confirmation", args=[payment.id]))
My model Payment:
class Payment(models.Model):
...
...
def submit(self, redirect_url):
'''
Sends self as a Payment through PagSeguro API.
Payment instance MUST BE SAVED before calling this method.
'''
if not self.id:
#Temporary to identify which problem caused the crash.
raise ValidationError
#creating a reference if its None
if self.reference is None:
self.reference = configs.PAGSEGURO_REFERENCE_PREFIX + str(self.id)
document = Document()
document.appendChild(self.to_element(document, redirect_url))
response = document2dict(api.submit_payment(document))
try:
self.code = response['checkout']['code']
self.answered_on = datetime.datetime.now()
self.save()
except:
error_str = ""
if type(response["errors"]["error"]) != list:
response["errors"]["error"] = [response["errors"]["error"]]
for error in response["errors"]["error"]:
error_payment = ErrorPayment()
error_payment.code = int(error['code'])
error_payment.payment = self
error_payment.save()
error_str += "[%s: %s]" % (error_payment.code,
error_payment.get_code_display())
raise Exception(error_str)
the error is here payment.submit (settings.SITE_URL + reverse ("shopping.views.payment_confirmation", args = [payment.id]))
I', using this api https://bitbucket.org/leonardosantos/django-pagseguro2/
This line: reverse("shopping.views.payment_confirmation", args=[payment.id]) tells Django to find a url that matches a method called payment_confirmation in the views.py file in the shopping app that will accept a payment ID parameter.
In the error that you shared, the payment.id was 35. The error is saying that either there is no method called payment_confirmation in your shopping.views or the method does not accept a single int as a parameter.
You didn't share a payment_confirmation method in your views file so it seems that that is the issue. You need to add:
def payment_confirmation(payment_id):
#do stuff
to your views.

Python Django getattr(): attribute name must be string

I am using python, Django and get the following error:
getattr(): attribute name must be string
location: val = getattr(obj, field)
if field in headers:
if not isinstance(field, str):
val = getattr(obj, field)
else:
val = getattr(obj, field.LastName)
if callable(val):
val = val()
if type(val) == unicode:
val = val.encode("utf-8")
row.append(val)
I have tried many variation of code but all failed.
You can confirm the object type of field by using print(type(field)). It will likely not be a string considering the error.
Looking at your code, it looks like field will be an object with attributes that are strings, such as LastName. The line
val = getattr(obj, field)
would probably be better off reading
val = getattr(obj, field.someattribute)
If field.someattribute is not a string, you can cast it to string using
str(field.someattribute)
For a grand total of val = getattr(obj, str(field.someattribute))
May be you are missing the attribute name.
I was getting the same error for this
price = models.DecimalField(15, 2,)
but when I modified the code like below error was resolved.
price = models.DecimalField(max_digits=15, decimal_places=2,)

to_python() never gets called (even in case of __metaclass__ = models.SubfieldBase)

Some time ago, as part of the process of learning Python+Django, I decided to write a custom MySQL-specific model field for the BIT column type. Unfortunately, I've ran into a problem.
The project: contains a single "main" app
The "main" app: contains all of the standard files created by "python manage.py startapp", plus extfields.py
The contents of extfields.py are as follows:
from django.db import models
import re
import bitstring
class BitField(models.Field):
description = 'A class representing a field of type "BIT" (MySQL-specific)'
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
bf_size = int(kwargs.pop('bitfield_size', 0))
assert bf_size > 0, '%ss must have a positive bitfield_size' % self.__class__.__name__
self._bf_size = bf_size
super(BitField, self).__init__(*args, **kwargs)
def db_type(self):
return 'BIT(%d)' % self._bf_size
def to_python(self, value):
print('to_python starts')
if isinstance(value, bitstring.BitArray):
return value
value = str(value)
regex = re.compile('^[01]{%d}$' % self._bf_size)
assert regex.search(value) == True, 'The value must be a bit string.'
print('to_python ends')
return bitstring.BitArray(bin=value)
def get_db_prep_value(self, value):
return value.bin
The contents of models.py:
from django.db import models
import extfields
class MessageManager(models.Manager):
"""
This manager is solely for the Message model. We need to alter the default
QuerySet so that we'll get the correct values for the attributes bit field
"""
def get_query_set(self):
return super(MessageManager, self).get_query_set().defer(
'attributes'
).extra(
select={'attributes': 'BIN(attributes)'}
)
class Message(models.Model):
attributes = extfields.BitField(bitfield_size=15)
objects = MessageManager()
When I use the python shell (via python manage.py shell), I get the following:
>>> from main import models
>>> m = models.Message.objects.get(pk=1)
>>> m
<Message_Deferred_attributes: Message_Deferred_attributes object>
>>> m.attributes
u'1110001110'
>>> type(m.attributes)
<type 'unicode'>
>>> m.attributes = '1312312'
>>> m.attributes
'1312312'
As you see, the m.attributes is a plain string, instead of bitstring.BitArray instance.
Could someone please tell me where I've made a mistake?
I'm using Python 2.6.5 on Ubuntu 10.04. The bitstring module I import is this one: http://code.google.com/p/python-bitstring/. Python-django package version is 1.1.1-2ubuntu1.3.
EDIT (in response to emulbreh's comment):
right now my to_python() definition looks like this:
def to_python(self, value):
print 'to_python'
if isinstance(value, bitstring.BitArray):
return value
print type(value)
value = str(value)
print'\n'
print value
print '\n'
print type(value)
regex = re.compile('^[01]{%d}$' % self._bf_size)
assert regex.search(value) == True, 'The value must be a bit string.'
value = bitstring.BitArray(bin=value)
print '\n'
print type(value)
print 'End of to_python'
return value
The console output is:
After this, an AssertionError is raised.
You don't need to do anything special in the QuerySet to support a custom field. You currently defer() your field and then add a raw extra(select=) attribute that coincidentally has the same name as your field.
If you just remove the custom manager (or the .defer().extra() calls) you should be fine.

Categories