add prefix to id in template if flag is set in django - python

My database was something like this :
| id | customer_account | -few more fields - | is_renewed |
| 25 | asd111 | - some values - | 0 |
| 26 | asd222 | - some values - | 1 |
| 27 | asd333 | - some values - | 1 |
| 28 | asd444 | - some values - | 0 |
in my models, I have :
class Policy(models.Model):
customer_account = models.ForeignKey(CustomerAccount, on_delete=models.CASCADE)
--few more fields--
is_renewed = models.BooleanField(default = False)
def use_updated_id(self):
if self.is_renewed:
new_id = str("R") + str(self.id)
else:
new_id = self.id
return new_id
in my template, I have :
{% for policy in policy_list % }
<p> Policy Number : {{policy.id}} </p>
{% endfor %}
which gives me output as
Policy Number : 25
Policy Number : 26
Policy Number : 27
Policy Number : 28
I understand that I can define a method in model and use that instead of id as below to meet my requirement
{% for policy in policy_list % }
<p> Policy Number : {{policy.use_updated_id}} </p>
{% endfor %}
Policy Number : 25
Policy Number : R26
Policy Number : R27
Policy Number : 28
My only challenge is that if use model method as above, i will have to replace updated multiple templates.
I'm looking for a better solution where in i only have to make changes in models file instead of updating multiple templates to achieve the desired result.

So you have {{ policy.id }} in multiple templates and want to change its behavior by making changes to models.py?
AFAIK you cannot achieve that, since you haven't correctly encapsulated the display beforehand. That's a pain, but you'll have to change it everywhere, since you're accessing a particular attribute on your models. Adding your use_updated_id is a great idea, since it encapsulates the display logic in one function and, in the future, if you need to change the display all you have to do is to change your new function.
So go on, make those hundreds of file edits but be sure that now you've made a great progress and facilitated your project maintainability.

Related

How to use behave context.table with key value table?

I saw that it is possible to access data from context.table from Behave when the table described in the BDD has a header. for example:
Scenario: Add new Expense
Given the user fill out the required fields
| item | name | amount |
| Wine | Julie | 30.00 |
To access this code it's simply:
for row in context.table:
context.page.fill_item(row['item'])
context.page.fill_name(row['name'])
context.page.fill_amount(row['amount'])
That works well and it's very clean, however, I have to refactor code when I have a huge amount of lines of input data. for example:
Given I am on registration page
When I fill "test#test.com" for email address
And I fill "test" for password
And I fill "Didier" for first name
And I fill "Dubois" for last name
And I fill "946132795" for phone number
And I fill "456456456" for mobile phon
And I fill "Company name" for company name
And I fill "Avenue Victor Hugo 1" for address
And I fill "97123" for postal code
And I fill "Lyon" for city
And I select "France" country
...
15 more lines for filling the form
How could I use the following table in behave:
|first name | didier |
|last name | Dubois |
|phone| 4564564564 |
So on ...
How would my step definition look like?
To use a vertical table rather than a horizontal table, you need to process each row as its own field. The table still needs a header row:
When I fill in the registration form with:
| Field | Value |
| first name | Didier |
| last name | Dubois |
| country | France |
| ... | ... |
In your step definition, loop over the table rows and call a method on your selenium page model:
for row in context.table
context.page.fill_field(row['Field'], row['Value'])
The Selenium page model method needs to do something based on the field name:
def fill_field(self, field, value)
if field == 'first name':
self.first_name.send_keys(value)
elif field == 'last name':
self.last_name.send_keys(value)
elif field == 'country':
# should be instance of SelectElement
self.country.select_by_text(value)
elif
...
else:
raise NameError(f'Field {field} is not valid')

Django - Saving one form multiple times

I have a Django view that uses one form multiple times. The form is saving relationship Subgroup id as a foreign key and Student id as a foreign key .
The problem I'm having is when I try to save information to database it only saves the last record.
For example (database model):
1 858 | Pump | Iron
2 78 | Madagaskar| Thomas
And if Im trying to split them into seperate groups, only Madagaskar his data is saved:
id | timestamp | student_Id_id | subgroup_Id_id |
+----+----------------------------+---------------+----------------+
| 62 | 2016-05-06 10:54:49.022000 | 2 | 91 |
The form looks like this:
class ApplicationFormaFull1(MultiModelForm):
form_classes = {
'sub1': FormSubgroup,
'sub2': FormSubgroup,
'stud_sub': FormStudent_in_Subgroup
}
and my view :
sub = form['sub1'].save(commit=False)
sub.student_group = StudentGroup.objects.get(id=element)
sub.number = 1
sub.type = 'Other'
sub.student_count = firstSubgroup
sub.save()
sub1 = form['sub2'].save(commit=False)
sub1.student_group = StudentGroup.objects.get(id=element)
sub1.number = 2
sub1.type = 'Others'
sub1.student_count = secondSubgroup
sub1.save()
if (counter%2==1):
stud_sub = form['stud_sub'].save(commit=True)
stud_sub.subgroup_Id = sub
stud_sub.student_Id = Student.objects.get(id=student)
stud_sub.save()
else:
stud_sub = form['stud_sub'].save(commit=True)
stud_sub.subgroup_Id = sub1
stud_sub.student_Id = Student.objects.get(id=student)
stud_sub.save()
So to sum up, I want that every form would save its information multiple times (dynamically)
Maybe the solution is that I should store information in the list and after all forms are added, save them one by one ?
stud_sub = form['stud_sub'].save(commit=False)
stud_sub.subgroup_Id = sub
stud_sub.student_Id = Student.objects.get(id=student)
list.add(stud_sub)
...
for i in list:
i.save()
Other solution use formset:
ArticleFormSet = formset_factory(ArticleForm, extra=2)
formset = ArticleFormSet(initial=[
{'title': 'Django is now open source',
'pub_date': datetime.date.today(),}
])
However i dont know how to change title, pub_date and to add everyting to formset dynimically.

Parsing recursive templates with pyparsing

I'm currently trying to parse recursive templates with pyparsing. A template can look like this:
{{Attribute
| name=attr1
| description=First attribute.}}
The template has a name (Attribute) and defines some variables (name = attr1, description = First attribute.). However, there are also templates which can contain zero or more templates:
{{Enum
| name=MyEnum
| description=Just a test enum.
| example=Not given...
| attributes={{Attribute
| name=attr1
| description=First attribute.}}
{{Attribute
| name=attr2
| description=Second attribute.}}}}
To parse these templates I came up with the following:
template = Forward()
lb = '{{'
rb = '}}'
template_name = Word(alphas)
variable = Word(alphas)
value = CharsNotIn('|{}=') | Group(ZeroOrMore(template))
member = Group(Suppress('|') + variable + Suppress('=') + value)
members = Group(OneOrMore(member))
template << Suppress(lb) + Group(template_name + members) + Suppress(rb)
This works quite well, but it does not allow me to use "|{}=" within a value which is problematic if I want to use them. E.g.:
{{Enum
| name=MyEnum
| description=Just a test enum.
| example=<python>x = 1</python>
| attributes=}}
So, how can I change my code so that it allows these characters, too? Unforunately, I have no idea how I can archieve this.
I hope someone can give me some tips!
I found what I was looking for: https://github.com/earwig/mwparserfromhell

Django aggregation across multiple tables in ModelAdmin queryset

Django Code & Reference to Django Bug Report
Given three models as follows (simplified excessively for demonstration...not actually identical related models)
class derp(models.Model):
...
class derp_related_1(models.Model):
fk = models.ForeignKey(derp)
amount = models.DecimalField(max_digits=15, decimal_places=2)
class derp_related_2(models.Model):
fk = models.ForeignKey(derp)
amount = models.DecimalField(max_digits=15, decimal_places=2)
And overriding a queryset in the model admin as follows. (It isn't working because of this django bug.)
class DerpAdmin(admin.ModelAdmin):
...
list_display = ['derp_r1_sum', 'derp_r2_sum']
...
def queryset(self, request):
qs = super(DerpAdmin, self).queryset(request)
qs = qs.annotate(derp_r1_sum=models.Sum('derp_r1__amount', distinct=True))
qs = qs.annotate(derp_r2_sum=models.Sum('derp_r2__amount', distinct=True))
def derp_r1_sum(self, obj):
return u'%s' % obj.derp_r1_sum
def derp_r2_sum(self, obj):
return u'%s' % obj.derp_r2_sum
Example of Unexpected Database Result
Running annotations individually would render something like (with grouping & sums removed)
+---------+--------+
| derp.id | r1_sum |
+---------+--------+
| 2 | 500.00 |
| 2 | 100.00 |
+---------+--------+
r1_sum would be 600.00
and
+---------+--------+
| derp.id | r1_sum |
+---------+--------+
| 2 | 100.00 |
| 2 | 250.00 |
+---------+--------+
r2_sum would be 350.00
If you take qs.query with both annotations included and remove the sums and the grouping it is obvious what the problem is. In this case we're counting everything twice. Get more relations and we have an increasingly ugly increase in both sum columns.
+---------+--------+--------+
| derp.id | r1_sum | r2_sum |
+---------+--------+--------+
| 2 | 500.00 | 100.00 |
| 2 | 500.00 | 250.00 |
| 2 | 100.00 | 100.00 |
| 2 | 100.00 | 250.00 |
+---------+--------+--------+
r1_sum would incorrectly be 1200.00
r2_sum would incorrectly be 700.00
Question, is there a route other than custom SQL?
I can write the query myself easy enough, but if anyone has a suggestion which would avoid the writing of custom SQL that would be awesome.
Thanks for the help.
Edit: Here is a link to the annotations section of the Django documentation. One commenter mentioned the distinct option. This does not work, and I believe it is what is warned about at the bottom of the annotation section in the django documentation on annotation.
Edit2: The raw SQL idea likely is more difficult than I thought as derp.objects.raw('sql here') does not return the queryset object necessary for the admin to use it. Is there a way to use two queries (the real queryset plus a custom one doing the sums) and populate the listview from both? One suggestion I found (which I cannot find again now :S) suggested creating a view that maps to a Model definition which is then set to unmanaged by django (for syncdb). I could then write my custom code, and reference it for inclusion in the original query. This sounds messy. Thoughts?
If you want to stay within Django's queryset, I would consider creating a model superclass that shares the related and common fields and sub-classing for further distinctions. Otherwise, you need to either write custom SQL or get out of the database ORM entirely and manipulate your data in python with Queryset.values or Queryset.values_list
The best way I found to return the correct results was by using queryset.extra().
derp_r1_sum_select = """
select sum(`derp_related_1`.`amount`)
from `derp_related_1`
where `derp_related_1`.`fk` = `derp`.`pk`
"""
derp_r2_sum_select = """
select sum(`derp_related_2`.`amount`)
from `derp_related_2`
where `derp_related_2`.`fk` = `derp`.`pk`"
"""
def queryset(self, request):
qs = super(DerpAdmin, self).queryset(request)
qs = qs.extra(select={'derp_r1_sum': derp_r1_sum_select,
'derp_r2_sum': derp_r2_sum_select})
return qs

HTML ─ Python dictionary from form-subelement via CherryPy

# ! /usr/bin/env python
# -*- coding: utf-8 -*-
# image_slide.py
""" Python 2.7.3
Cherrypy 3.2.2
"""
When an HTML form is submitted to a Python function, the values are sent within one dictionary. For some subelements in an HTML form, I want to POST an additional nested dictionary, within that form dictionary. The question is, if there is an HTML tag or such a thing, which Python would interpret as opening for a new dictionary. So far I tried naming the <td> or an invisible <fieldset>, but of course it isn't as simple. And nested forms aren't possible either.
What I have is a form, which contains a few text inputs and a table with a selection of associated items.
_____ the entire form __________________________________________________________
| |
| beverage: coffee |
| time of day: morning |
| temperature: hot |
| color: brown |
| taste: full-bodied |
| |
| Ingredients: |
| ________________________________________________________________________ |
| | | | | | |
| | <IMAGE_1> | <IMAGE_2> | <IMAGE_3> | <IMAG| |
| | | | | | |
| | filename: coffee | filename: sugar | filename: milk | filename:| |
| | x select item | x select item | x select item | x select | |
| |___________________|___________________|___________________|__________| |
| |_____________________________<scrollbar>______________________________| |
| |
| < OK > |
|______________________________________________________________________________|
Following are three simple pseudo-code snippets to explain the gist.
Content of one form entry:
beverages = {
'coffee': {
'beverage': 'coffee',
'time of day': 'morning',
'temperature': 'hot',
'color': 'brown',
'taste': 'full-bodied',
}
}
ingredients = {
'coffee': [
'coffee',
'sugar',
'milk',
'cinnamon',
],
}
Top part of the form :
yield(u'''
<form action="..." method="POST">
<table>
''')
for key, value in beverages['coffee'].items():
yield(u'''
<tr>
<td> {key}: </td>
<td> <input type="text" name="{key}" value="{value}">< /td>
</tr>
'''.format(locals(),)
Bottom part of the form :
""" This hole bottom part already shall be in a dictionary named 'ingredients'.
For each loop pass, I'd like a sub-dictionary, named by its loop pass.
I used the fictional tag <SPECIAL_SUB_DICT>, to open a new dictionary.
"""
yield(u'''
</table>
<SPECIAL_SUB_DICT name="ingredients">
<table style="overflow-x: scroll">
<tr>
''')
for index, name in enumerate(ingredients['coffee']):
yield(u'''
<td>
<SPECIAL_SUB_DICT name="{index}">
<img src="...">
<input type="text" name="filename" value="{name}">
<input type="check" name="chosen_one" value="{index}"> select item
</SPECIAL_SUB_DICT>
</td>
'''.format(locals(),)
yield(u'''
</tr>
</table>
</SPECIAL_SUB_DICT>
<input type="submit" name="submit" value="OK">
</form>
''')
Conclusion
The problem is, without some sort of <SPECIAL_SUB_DICT> tag, I can't easily rename single ingredients. For example, if I'd like to change the filename from 'milk' to 'hole cream milk'. The way I do it now is, to add the current loop pass to the input name, like this:
'<input type="text" name="filename_{index}" value="{name}"> '.format(locals(),)
Then, within the receiving function, I can check which key starts with 'filename_' and update all of them:
for key, values in kwargs:
if key startswith('filename_'):
ingredients['coffee'][key[9:]] = value
It would be much nicer, if I just could iterate kwargs['ingredients']:
for key, values in kwargs['ingredients'].items():
ingredients['coffee'][key] = value[filename]
I'm asking, because a <SPECIAL_SUB_DICT> tag would be much closer to my current solution than parsing the table with Python's BeautifulSoup. And of course I want to know it. After all, with BeautifulSoup I'd probably be done by now.
EDIT 1:
This runs with the web application framework CherryPy.
Maybe there's a way it could handle such a request.
Though I dont't think, that it would offer something off-standard.
EDIT 2:
Given, that the form-dictionary starts in the URL with a question mark, I don't think a sub-dictionary is possible, because I don't know of any dictionary closing character. The closest thing I could come up with is using a hidden input named 'index', and zip() it with the text input named 'filename'. This answer, unfortunately for PHP, got me in the right direction.
'<input type="hidden" name="index" value="{index}"> '.format(locals(),)
'<input type="text" name="filename" value="{name}"> '.format(locals(),)
for key, values in zip(kwargs['index'], kwargs['filename']):
ingredients['coffee'][key] = value
However, this wouldn't work for the delete button, which I planed to add to each ingredient. Since only the submit input can carry the index, and its value is used to display the button's unicode symbol, I again have to append the index to the name.
Then, the only thing I can think of, is a form for each ingredient and the top part of the formular, instead of one large form for everything. Though I don't know if that would be good for the performance, if the list of ingredients becomes too long.
As a visual trick comes a background image to mind, to replace the value, which then transparently, can be used according to its name. This answer and its corresponding question was helpful to get it working.
But nothing of this is an answer to my question.
I'm still missing the one solution for all inputs,
like multiple forms, just more elegant.
Okay, I wanted to know how this is done as well, so here you go. This only works for single-level dicts. If you want to make this recursive, I'll leave it to you.
The problem with this: <input name="foo[bar]"> is that foo[bar] is the variable name, and the brackets are converted to URL-safe entities:
[DEBUG] BODY_TEXT: foo%5Bbar%5D=input_value
CherryPy allows you to define a custom body processor by defining a callable in cherrypy.request.body.processors, which is a dictionary whose keys match the content type. This is how the cherrypy.tools.json_in() tool works, by replacing the body processor with a function that parses JSON when the request's content type is application/json.
The final part you need is how to inject your parsed dictionary (i.e. {'foo': 'bar'} into the handler method's arguments to be used just like regular POST input vars. I took it directly from the default form processor: https://github.com/cherrypy/cherrypy/blob/main/cherrypy/_cpreqbody.py#L177
So here's the tool. You need to enable it in your config, and load it in your server script, but it will take a dict-shaped set of form inputs pass the dict to your handler. It does this with a regular expression.
import re
import cherrypy
import logging
import urllib
class ImprovedFormInput(cherrypy.Tool):
def __init__(self):
logging.debug('ImprovedFormInput.__init__()')
cherrypy.Tool.__init__(self, 'before_request_body', self.handle_post, priority=50)
def handle_post(self):
logging.debug('ImprovedFormInput.handle_post()')
request = cherrypy.serving.request
def impfin_processor(entity):
raw_body = entity.fp.read()
body_text = raw_body.decode()
logging.debug('BODY_TEXT: %s', body_text)
parsed = urllib.parse.parse_qs(body_text)
logging.debug('PARSED: %s', parsed.keys())
form_inputs = {}
r = re.compile(r'^(\w+)\[(.+)\]$') # This pattern could be better
for key in parsed.keys():
m = r.match(key)
if m is None:
continue
groups = m.groups()
pkey = groups[0]
logging.debug('PKEY: %s', pkey)
ckey = groups[1]
logging.debug('CKEY: %s', ckey)
if pkey not in form_inputs:
form_inputs[pkey] = {}
form_inputs[pkey][ckey] = parsed[key]
logging.debug('FINPUTS: %s', form_inputs)
# https://github.com/cherrypy/cherrypy/blob/main/cherrypy/_cpreqbody.py#L177
for key, value in form_inputs.items():
if key in entity.params:
if not isinstance(entity.params[key], list):
entity.params[key] = [entity.params[key]]
entity.params[key].append(value)
else:
entity.params[key] = value
request.body.processors['application/x-www-form-urlencoded'] = impfin_processor

Categories