Custom Number Field is not executing "to_python"? - django

I am new to Django and Python.
In my project I am using CustomField defined by me so that I can encrypt the data values before storing into database and decrypt it after retreiving (there is need for encryption in my case).
I have gone through git-repository / stackoverflow / google to find the answer for my question, there are similar questions on stackoverflow but none of them got this issue. As I am unable to fix it from 2 days, would certainly need help now.
I have the code which is defining CustomField as follows -
def get_multiple_of_8(some_value):
some_value = str(some_value)
while(len(some_value) % 8 != 0):
some_value = '0'+some_value
return some_value
def is_multiple_of_8(input_str):
input_str = str(input_str)
if(len(input_str) % 8 == 0):
return True
return False
class CustomField(models.Field):
'''To Encrypt the values in Model'''
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
self.key = myProject.settings.SECRET_KEY[:8]
self.des = DES.new(self.key, DES.MODE_ECB)
kwargs['max_length'] = kwargs.get('max_length',128)
models.Field.__init__(self, *args, **kwargs)
def get_internal_type(self):
return 'CharField'
def get_prep_value(self, value):
if value is None:
return None
value = "%.2f" % value
value = get_multiple_of_8(value)
to_return = self.des.encrypt(value)
to_return = to_return.decode('latin-1')
# to_return = force_unicode(to_return)
return to_return
def to_python(self, value):
value = value.encode('latin-1')
if is_multiple_of_8(value):
to_return = self.des.decrypt(value)
try:
to_return = float(to_return)
return to_return
except:
return 0
Encryption works perfectly, I can see the encrypted field values using sqlite3 command line.
error I am getting is :
Exception Type: InvalidOperation
Exception Value: Invalid literal for Decimal: '\xc3\x85\xc3\x84\xc3\xa13\xc2\xa6\x0f(u'
On debugging found that "to_python()" is not functioning properly. (Code source from django-fields git). I am getting error which shows encrypted string on screen instead of actual value upon retrieval through views.py !
I found one question on stackoverflow having same problem but got fixed because of metaclass syntax in different Python version.
My Python version 2.7.5 and Django 1.4.5, I am developing site on Windows 7.
Please someone help me resolve the issue, also Is there a way to debug these kind of issues ?, any suggestion is accepted, thank you in advance...

Finally got the answer :)
Had to understand how the SQLite database system works!
The problem here is, when the Database which we are using, let it be any in that case. Firstly we shall format the data to be stored into it. Once the types are assigned to the database table fields, (I don't know why) still it can save data of other format (May be bug in SQLite, table had Integer field but still stored 350 characters long string, command line displayed). Problem arises when retrieval is done, Database SW will look for Integer value from the field but gets some non-supported values. (Hence error).
May be my question now shifted to the one in brackets in fore mentioned paragraph ? Is it true (because it proved like that to me) or Is there other reasons ?

Related

Get slug name list by url path name

I am reverse-engineering django urlpatterns. For my purpose, I need to dynamically know the list of slug names developers chose to use within url. (for weird decorator purposes outside of scope of this question)
Example:
path("<slug:membership_slug>/<slug:parent_slug>/data/", rzd.ReportingUnitDataView.as_view(), name="hierarchy.item.data"),
So, in the world, where everyone writes perfect code, my function whould take "hierarcy.item.data" and spit out ["membership_slug", "parent_slug"]
I looked at the reverse function, hoping it would return something useful without kwargs, but it does not. Have been googling like Neo, but the hope does not leave my heart... somewhere django should hold all that urlpatterns stuff! I still believe.
Perhaps you can at suggest how to get a hold of list of urls, even if in the raw format, that would already be a help. Thank you.
Search for book called Django 2x scopes this book hold every thing related to django best practices and of course the urls part.
This book will help you to understand more how django developers think.
Happy hacking 🙃
I think you're best guess is to write a Regex..
From what I've found in the source code they use _lazy_re_compile(r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>') which can be found in urls\resolvers.py in _route_to_regex
this is what I've come up with after butchering the source code
def geturlnamedarguments(route):
from django.utils.regex_helper import _lazy_re_compile
named_arguments = []
while True:
match = _lazy_re_compile(r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>').search(route)
if not match:
break
route = route[match.end():]
parameter = match['parameter']
named_arguments.append(parameter)
return named_arguments
You can also get the raw URL route as a string by doing
urlpatterns[index].pattern._route
You can find the URLPattern Object also in urls\resolvers.py if you want to play around with it.. I never found a place where they have the names in a raw format
Solved, It occurred to me, that I can just include urls.py and parse it out myself :)
from myapp import urls
class UrlPatterns:
#staticmethod
def _get_params(s):
items = []
is_exp = False
is_prefix = False
type = ''
name = ''
for n in range(len(s)):
if not is_exp:
if s[n] == '<':
is_exp = True
is_prefix = True
type = ''
elif is_prefix:
if s[n] == ':':
is_prefix = False
else:
type += s[n]
else:
if s[n] == '>':
is_exp = False
items.append({
'type': type,
'name': name
})
name = ''
else:
name += s[n]
return items
#staticmethod
def _find_pattern(url_name):
for url in urls.urlpatterns:
if url.name == url_name:
return str(url.pattern)
return None
#staticmethod
def get_required_params(url_name):
url_pattern = UrlPatterns._find_pattern(url_name)
if url_pattern:
return UrlPatterns._get_params(url_pattern)
return None

Can't save custom optional ChooserBlock when blank

Came across the very same issue that is well described here : https://github.com/wagtail/wagtail/issues/7344#issue-946329708 but the fix doesn't seem to work in my case.
Basically I have a custom ChooserBlock supposed to make it possible to tie a model's given instance to a StructBlock. Rendering works differently if the ChooserBlock value is blank or not, so it is quite important that it actually can be blank.
But when left blank i stamble upon this error when trying to save :
Field 'id' expected a number but got ''.
The error's stack is exactly the same as the one depicted in the github issue I linked above.
I tried the given fix but it doesn't seem to make any difference, at least for my issue.
class CommunityChooserBlock(blocks.ChooserBlock):
target_model = Community
widget = forms.Select
# Return the key value for the select field
def value_for_form(self, value):
if value == "":
return None
else:
return super().value_from_form(value)
Did I miss anything ?
Edit:
I've also tried to override the get_prep_value and clean methods but it didn't change anything.
def get_prep_value(self, value):
if value == '':
return None
else:
super().get_prep_value(value)
def clean(self, value):
if value == '':
value = None
super().clean(value)
Update : I couldn't come up with a proper fix, so I changed my plans and went for a ChoiceBlock with dynamic choices list, as described here : https://stackoverflow.com/a/60979072/13934028.
It seems to work just fine for my case

How to schedule task to run in background using django background tasks

I have set up a background task "date_check" but it does not run daily. I can't figure out where I've went wrong in the setup. I also got the error "TypeError: date_check() missing 1 required positional argument: 'self' when I tried to run python manage.py process_tasks.
here is my model.py:
...
#background(schedule=5)
def date_check(self):
"""Will be used as a background task to make sure trips that have ended don't hog van availability."""
today = datetime.date.today()
name = self.van_used
if today > self.trip_start and today > self.trip_end:
vans.objects.filter(vanName = name).update(available = True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.date_check(repeat=Task.DAILY)
UPDATE:
I tried removing self from the function parameter, still to no avail. I am now getting the error TypeError: date_check() missing 3 required positional arguments: 'van_used', 'trip_start', and 'trip_end'.
Here's the updated code:
#background(schedule=5)
def date_check(van_used, trip_start, trip_end):
"""Will be used as a background task to make sure trips that have ended don't hog van availability."""
today = datetime.date.today()
name = van_used
if today > trip_start and today > trip_end:
vans.objects.filter(vanName = name).update(available = True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.date_check(self.van_used, self.trip_start, self.trip_end, repeat=Task.DAILY)
In order to actually run the function, it (of course) needs the arguments. There is, however, not really a position in your code that calls it (since its scheduled), meaning there is not really a position in your code where you can actually pass the arguments. This is also what Willem meant in his reply when he mentioned it was a "standalone", you don't call it yourself. Since python doesn't know what to pass but is asked to give vars, it throws an exception.
The answer: find a way to retrieve the argument within the function. Make the signature arg-free. I'm guessing the "self" in your original post is a "Van"-object, so you'd get something like this:
def date_check():
"""Will be used as a background task to make sure trips that have ended don't hog van availability."""
van = vans.objects.all() # or some appropriate filter
today = datetime.date.today()
for v in van:
name = van.van_used
if today > van.trip_start and today > van.trip_end:
vans.objects.filter(vanName = name).update(available = True)
There's not really a way to make a background task execute on all vans individually (not one that I know of at least) but this should give you the same result

Ensure User Entered Integer

I am new to Django, and I recently created a system where users can look up a record based on a number. It's just a simple search. The system numbers have leading zeros, and I want the system to recognize the numbers with or without the zeros. I have been able to implement this system and I am converting the number the user specifies with the following code:
def get_queryset(self):
queryset = super(SearchResultsView, self).get_queryset()
search_query = int(self.request.GET.get("q"))
if search_query:
queryset = Book.objects.filter(Q(request_number__icontains=search_query)).distinct()
The code above works fine, as long as the user enters a number. If they typo and include letters, I get invalid literal for Base10. I understand the error, a letter is not an INT. I have spent most of the afternoon looking for how to prevent this error and I can't find what I'm looking for. I have tried to do something like:
if search_query:
try:
queryset = Book.objects.filter(Q(request_number__icontains=search_query)).distinct()
except ValueError:
q = 0000000
return queryset
But the letters are still interpreted and then I receive the invalid literal for Base10 error again. How can I prevent the letters from causing a problem with my query based on a number?
I have also figured out that if I remove the conversion to INT for the search query, the letters no longer cause a problem and the system returns nothing as I would expect it to so I have a work around. Just wondering how I could get the system to do both, accept the letters and also then prevent the invalid literal error and allow the system to turn the input into integers. Thanks in advance for your helpful suggestions.
As Daniel Roseman suggested, I tried to use the following form, but it doesn't seem to catch the error either...
class RequestNumberSearch(forms.Form):
q = forms.IntegerField(required=True)
def __init__(self, user, *args, **kwargs):
super(RequestNumberSearch, self).__init__(*args, **kwargs)
self.fields['q'].widget.attrs['class'] = 'name2'
def clean_q(self):
data = self.cleaned_data['q']
if q != int:
raise forms.ValidationError("Please enter valid numbers!")
return data
You are trying to cast the query to int before checking it.
search_query = self.request.GET.get("q")
if search_query.isdigit(): # check is digit
queryset = Book.objects.filter(Q(request_number__icontains=search_query)).distinct()
return queryset
elif ... : # another check
...
else:
return 'query is erroneous'

Setting the selected value on a Django forms.ChoiceField

Here is the field declaration in a form:
max_number = forms.ChoiceField(widget = forms.Select(),
choices = ([('1','1'), ('2','2'),('3','3'), ]), initial='3', required = True,)
I would like to set the initial value to be 3 and this doesn't seem to work. I have played about with the param, quotes/no quotes, etc... but no change.
Could anyone give me a definitive answer if it is possible? And/or the necessary tweak in my code snippet?
I am using Django 1.0
Try setting the initial value when you instantiate the form:
form = MyForm(initial={'max_number': '3'})
This doesn't touch on the immediate question at hand, but this Q/A comes up for searches related to trying to assign the selected value to a ChoiceField.
If you have already called super().__init__ in your Form class, you should update the form.initial dictionary, not the field.initial property. If you study form.initial (e.g. print self.initial after the call to super().__init__), it will contain values for all the fields. Having a value of None in that dict will override the field.initial value.
e.g.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
# assign a (computed, I assume) default value to the choice field
self.initial['choices_field_name'] = 'default value'
# you should NOT do this:
self.fields['choices_field_name'].initial = 'default value'
You can also do the following. in your form class def:
max_number = forms.ChoiceField(widget = forms.Select(),
choices = ([('1','1'), ('2','2'),('3','3'), ]), initial='3', required = True,)
then when calling the form in your view you can dynamically set both initial choices and choice list.
yourFormInstance = YourFormClass()
yourFormInstance.fields['max_number'].choices = [(1,1),(2,2),(3,3)]
yourFormInstance.fields['max_number'].initial = [1]
Note: the initial values has to be a list and the choices has to be 2-tuples, in my example above i have a list of 2-tuples. Hope this helps.
I ran into this problem as well, and figured out that the problem is in the browser. When you refresh the browser is re-populating the form with the same values as before, ignoring the checked field. If you view source, you'll see the checked value is correct. Or put your cursor in your browser's URL field and hit enter. That will re-load the form from scratch.
Both Tom and Burton's answers work for me eventually, but I had a little trouble figuring out how to apply them to a ModelChoiceField.
The only trick to it is that the choices are stored as tuples of (<model's ID>, <model's unicode repr>), so if you want to set the initial model selection, you pass the model's ID as the initial value, not the object itself or it's name or anything else. Then it's as simple as:
form = EmployeeForm(initial={'manager': manager_employee_id})
Alternatively the initial argument can be ignored in place of an extra line with:
form.fields['manager'].initial = manager_employee_id
Dave - any luck finding a solution to the browser problem? Is there a way to force a refresh?
As for the original problem, try the following when initializing the form:
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.base_fields['MyChoiceField'].initial = initial_value
To be sure I need to see how you're rendering the form. The initial value is only used in a unbound form, if it's bound and a value for that field is not included nothing will be selected.