Views in Django with arbitrary number of url parameters - python

I want to write universal View with Django, in this function i want to handle several situations: first when i have url like vkusers3/11122233/1/2/ and also i want it working when 2 or third arguments is missing in url, like: vkusers3/11122233/ or vkusers3/11122233/1/
I cannot find it tutorials how to do that (https://docs.djangoproject.com/en/1.6/topics/http/urls/ etc).
The problem that this became a nightmare when you have more than 5 combinations in url parameters, then you should write 5 different url configurations, 5 times in html template hardcode this pattern.
BUT wait, even more!, what about combinatorics: i want /user/group/sex/smoking/ but also i want /user/group/smoking/ i.e. all users from group who is smoking of both man and woman. So the number is huge.
def list_groupmembers_sex(request, group_id, sex=None, smoking=None):
success = False
if group_id and sex and smoking==None:
vkusers = Vkuser._get_collection().find({"member_of_group": int(group_id), 'sex': int(sex)})# 62740364 81099158
success = True
elif group_id and sex and smoking!=None:
vkusers = Vkuser._get_collection().find({"member_of_group": int(group_id), 'sex': int(sex), 'personal.smoking': int(smoking)})
success = True
else:
vkusers = Vkuser._get_collection().find({'personal.smoking': 1})
ctx = {'vkuser_list': vkusers, 'group_id': group_id, 'sex': sex, 'smoking':smoking, 'success': success}
return render_to_response('blog/vkuser_list.html', ctx, context_instance = RequestContext(request))
In my urls.py:
url(r'^vkusers3/(\d{1,8})/(\d{1})/(\d{1})/$', 'blog.views.list_groupmembers_sex', name='vkuser-list3'),
In my base.html:
<li class="menu-level-1">users</li>
Django 1.6.10, MongoDB 2.7.1, mongoengine

At this point, you should probably bite the bullet and just go for query parameters - vkusers3/?sex=1&smoking=2&group= 11122233. You can drop the parameters completely from the URL and the view definition, and just use request.GET['sex'] etc in the view body.

You don't need so hairy logic. Just populate the search critera with arguments passed to the view like this:
criteria = {}
if group_id:
criteria['member_of_group'] = int(group_id)
if sex:
criteria['sex'] = int(sex)
if smoking:
criteria['personal.smoking'] = int(smoking)
vkusers = Vkuser._get_collection().find(criteria)
And yes, consider to switch to the regular GET parameters like #daniel-roseman suggested. With urls like in your question you can't determine the /user/group/sex/ url from the /user/group/smoking/.
UPDATE: request.GET is a dict-like object so you can use the in expression:
if 'sex' in request.GET:
criteria['sex'] = int(request.GET['sex'])

Related

Dynamically pass arguments to objects.filter

I am pretty new to Django and I am trying to get a query set from a filter function. This filter function is supposed to be able to take 1 to 5 arguments and I am not sure how to handle that.
I have not found anything here that might help me, so if you do know of some other question that might help please let me know.
views.py:
#api_view(('Get',))
def update(request, REQUEST):
if request.method == "Get":
requestlist = REQUEST.split('&')
for keys in requestlist:
if 'module' in keys:
module = keys[8:]
if 'value' in keys:
value = keys[6:]
if 'user' in keys:
user= keys[5:]
if 'time1' in keys:
time1 = keys[6:]
if 'time2' in keys:
time2 = keys[6:]
item = Post.objects.filter(name=Name, user=USER, ...)
The full request string will look like name=NAME&value=VALUE&user=USER&time1=FIRSTTIME&time2=SECONDTIME but it could also be any combination of the individual variables like name&time1.
Now I want to be able to do that with one filter method instead of creating like 2^5 for each different szenario.
The full request string will look like name=NAME&value=VALUE&user=USER&time1=FIRSTTIME&time2=SECONDTIME.
This is a query string [wiki], and Django automatically parses this to a dictionary-like QueryDict, you thus should not specify this yourself. You can work with:
if request.method == 'GET':
Post.objects.filter(**request.GET.dict())
I would however advise to only allow specific keys, and thus not all keys, since then the database is less secure: one can use the filtering mechanism to retrieve data.
It thus might be better to work with:
datas = {}
accept_keys = {'module', 'value', 'user', 'time1', 'time2'}
for key, value in request.GET.dict().items():
if key in accept_keys:
datas[key] = value
if request.method == 'GET':
Post.objects.filter(**datas)
In that case the item after the path is the query string, and the separator between the path and the query string is a question mark (?).
The path thus looks like:
urlpatterns = [
# …,
path('some/path/', views.update, name='update'),
# …
]
and you thus query the path with some.host.com/some/path?name=NAME&value=VALUE&user=USER&time1=FIRSTTIME&time2=SECONDTIME.

Rendering multiple dataframes from API calls in Django Views

I have a page that is meant to show company financials based on customers input(they input which company they're looking for). Once they submit, in the view function I want to create 5 API urls which then get the json data, create a list with the dates from 1 API result (they will all contain the same dates, so I will use the same list for all), then create new dictionaries with specific data from each api call, as well as the list of dates. I then want to create dataframes for each dictionary, then render each as html to the template.
My first attempt at this I attempted to do all requests.get and jsons.load calls one after another in a try block within " if request.method == 'POST' " block. This worked well when only grabbing data from one API call, but did not work with 5. I would get the local variable referenced before assigned error, which makes me think either the multiple requests.get or json.loads was creating the error.
My current attempt(which was created out of curiosity to see if it worked this way) does work as expected, but is obv not correct as it is calling the API multiple times in the for loop, as shown. (I have taken out some code for simplicity)
def get_financials(request, *args, **kwargs):
pd.options.display.float_format = '{:,.0f}'.format
IS_financials = {} #Income statement dictionary
BS_financials = {} #Balance sheet dictionary
dates = []
if request.method == 'POST':
ticker = request.POST['ticker']
IS_Url = APIURL1
BS_URL = APIURL2
try:
IS_r = requests.get(IS_Url)
IS = json.loads(IS_r.content)
for year in IS:
y = year['date']
dates.append(y)
for item in range(len(dates)):
IS_financials[dates[item]] = {}
IS_financials[dates[item]]['Revenue'] = IS[item]['revenue'] / thousands
IS_financials[dates[item]]["Cost of Revenue"] = IS[item]['costOfRevenue'] / thousands
IS_fundementals = pd.DataFrame.from_dict(IS_financials, orient="columns")
for item in range(len(dates)):
BS_r = requests.get(BS_URL)
BS = json.loads(BS_r.content)
BS_financials[dates[item]] = {}
BS_financials[dates[item]]['Cash and Equivalents'] = BS[item]['cashAndCashEquivalents'] / thousands
BS_financials[dates[item]]['Short Term Investments'] = BS[item]['shortTermInvestments'] / thousands
BS_fundementals = pd.DataFrame.from_dict(BS_financials, orient="columns")
except Exception as e:
apiList = "Error..."
return render(request, 'financials.html', {'IS': IS_fundementals.to_html(), 'BS': BS_fundementals.to_html()})
else:
return render(request, 'financials.html', {})
I'm trying to think of the proper way to do this. I'm new to django/python and not quite sure the best practice for a problem like this would be. I thought about making separate functions for each API, but then I would be unable to render them all on the same page. Can I use nested functions? Where only the main function renders to template, and all inner functions simply return the dataframe to outer function? Would class based views be better for something like this? I have never worked with class based views yet so would be a bit of a learning curve.
Another question I have is how to change the html in the table that is rendered from dataframe? The table/font that is currently rendered is quite large.
Thanks for any tips/advice!
It's not common to use pandas only for it's .to_html() method, but I have invoked pandas in a django method for less.
A more common approach is to loop over the IS and BS objects using django template's loop methods to generate the html tables.
To make this method more efficient move the BS api call out of the date loop, As long as the API call is not changed by the date.
Reasonable timeouts on the api calls would help also.
def get_financials(request, *args, **kwargs):
pd.options.display.float_format = '{:,.0f}'.format
IS_financials = {} #Income statement dictionary
BS_financials = {} #Balance sheet dictionary
dates = []
if request.method == 'POST':
ticker = request.POST['ticker']
IS_Url = APIURL1
BS_URL = APIURL2
try:
IS_r = requests.get(IS_Url, timeout=10)
IS = json.loads(IS_r.content)
BS_r = requests.get(BS_URL, timeout=10)
BS = json.loads(BS_r.content)
for year in IS:
y = year['date']
dates.append(y)
for item in range(len(dates)):
IS_financials[dates[item]] = {}
IS_financials[dates[item]]['Revenue'] = IS[item]['revenue'] / thousands
IS_financials[dates[item]]["Cost of Revenue"] = IS[item]['costOfRevenue'] / thousands
IS_fundementals = pd.DataFrame.from_dict(IS_financials, orient="columns")
for item in range(len(dates)):
BS_financials[dates[item]] = {}
BS_financials[dates[item]]['Cash and Equivalents'] = BS[item]['cashAndCashEquivalents'] / thousands
BS_financials[dates[item]]['Short Term Investments'] = BS[item]['shortTermInvestments'] / thousands
BS_fundementals = pd.DataFrame.from_dict(BS_financials, orient="columns")
except Exception as e:
apiList = "Error..."
return render(request, 'financials.html', {'IS': IS_fundementals.to_html(), 'BS': BS_fundementals.to_html()})
else:
return render(request, 'financials.html', {})

added two search bar in django one search by id and other by name .Tried many ways but cannot do it if i try by id then i am not able to do with name

I have to make 2 search bars. I don't know how to add multiple fiels...
match= Staff.objects.filter(id=srch1...) here how can I add name=srch1
over here after trying many ways I found it but the problem is all input here is string how to change it to int
def search(request):
# Catch the data and search in Staff model.
if request.method=='POST':
srch1 = request.POST['srch']
print(type(srch1))
if type(srch1)== int:
match= Staff.objects.filter(id=srch1)
if match :
return render(request,'search.html',{'sr': match})
else:
messages.error(request,'no results,found')
elif type(srch1)== str:
catch= Staff.objects.filter(name=srch1)
if catch:
return render(request,'search.html',{'sr': catch})
else:
messages.error(request,'no results,found')
else:
return HttpResponseRedirect("/search")
return render(request,"search.html")
You should be using a GET request with querysting parameters or (url parameters, which is a bit more complicated) for this, not a POST. Here's how I would do this:
def search(request, *args, **kwargs):
# Initial empty query dictionary for use with query later.
query = {}
# Getting 'name' and 'id' from querystring parameters (i.e. ?id=1&name=foo)
name = request.GET.get('name', None)
id = request.GET.get('id', None)
# Add 'id' to the query dictionary if it exists
if id is not None:
query['id'] = id
# Add name to the query dictionary if it exists
if name is not None:
query['name'] = name
# If the query dictionary has name or id, get the Staff entry from the database
if query.get('name', None) or query.get('id', None):
# Note that .filter() returns a QuerySet. You should probably use .get()
# since my guess is that you only want one Staff object (judging by your
# search parameters). Also note that since we are using **query we will be
# using BOTH 'name' AND 'id' to search for the Staff, as long as both exist in
# the query dictionary.
match = Staff.objects.get(**query)
# If there is a match, send it in the rendered response context dict
if match:
return render(request, 'search.html', {'sr': match})
# no match, send message notifying that a Staff entry was not found matching
# the desired criteria
return render(request, 'search.html', {message: 'Not Found'}
# There were no query parameters, so we are not searching for anything.
else:
return render(request, 'search.html')
You can see that the above code is much simpler, and more concise. This will help you or anyone else checking out your code in the future to better understand what you're trying to acheive.
P.S. I typically only use POST requests when I am creating an entry in the database. Maybe this is preference, but to my knowledge it is best practice to use a GET request for search.

Can I use same Form twice for django SessionWizard?

I am trying to create a SessionWizardView for a trip creation process. The trip might have one leg (one way) or two legs (round trip). Each leg has similar schema so I would like to use the same Form for both step 0 and step1, with a condition saying only to use step1 when the flight is round trip.
The problem I am having is that my "submit" button keeps loading step 0 over and over again instead of moving on to step 1 as it should for a round trip flight. (I am prepopulating each of the forms based on previously requested trip info for each leg in the get_form_initial() override). My form populates correctly for the first leg, it just populates the first leg data on every submit ad infinitum.
I could make two identical forms, but that seems like poor practice. Slightly better, I could have the Return trip form just inherit from the Outbound trip form and not make any changes to it - this is what I'll try next barring a better solution.
But, I'm really wondering if there is there a way to use the same form twice?
In my urls.py:
wizard_forms = [TripCreationForm,TripCreationForm]
urlpatterns = patterns('',
url(r'^trip/wizard/(?P<pk>\d+)$',
views.CreateTripSetView.as_view(wizard_forms,
condition_dict= {'1':show_return_trip_form}), name='admin_add_tripset')
)
in views.py:
def show_return_trip_form(wizard):
"""
Tells the CreateTripSetView wizard whether to show the return trip form
Args:
wizard:
Returns: True if this is a round trip, false if one-way
"""
cleaned_data = wizard.get_cleaned_data_for_step('0') or {}
if cleaned_data.get('total_legs') == 2:
return True
return False
class CreateTripSetView(SessionWizardView):
def get_form_initial(self, step):
"""
Populates the initial form data based on the request, route etc.
THIS IS ALWAYS FIRING FOR STEP=0 WHEN I HIT SUBMIT.
Args:
step:
Returns:
"""
initial = self.initial_dict.get(step, {})
triprequest = TripRequest.objects.filter(id=self.kwargs['pk']).first()
if triprequest is None:
return initial
initial.update({
'request_id': flight_request.id,
#other fields set on initial here
})
return initial
in forms.py:
class TripCreationForm
#field defs ex.
request_id = forms.IntegerField()
#etc.
def __init__(self, initial, *args, **kwargs):
object_data = {}
object_data['request_id'] = initial['request_id']
#etc.
super(AnywhereFlightCreationForm, self).__init__(initial=object_data, *args, **kwargs)
Edited:
So far I've been able to make this work using two subclasses of TripCreationForm but not using TripCreationForm for both.
Thanks in advance!
The wizard needs to identify them as separate steps. Maybe this would work?
wizard_forms = [
("form1", TripCreationForm),
("form2", TripCreationForm),
]

How to map two views with the same route in pyramid?

I have two views, the 'orderlist' and the 'orderview'. 'orderlist' will list all orders to user, while 'orderview' will show detailed information of one order.
Now I'd like to organize the URL like this:
/order map to orderlist and show all orders
/order/{id} map to orderview and show detailed info of one order
Is there anyway to implement this? Thanks.
This is just basic URL dispatch.
config.add_route('all_orders', '/order')
config.add_route('order_detail', '/order/{id}')
#view_config(route_name='all_orders', renderer='all_orders.mako')
def all_orders_view(request):
all_orders = {} # query the DB?
return {'orders': all_orders}
#view_config(route_name='order_detail', renderer='order_detail.mako')
def order_detail_view(request):
order_id = request.matchdict['id']
order = None # query the db for order
if order is None:
raise HTTPNotFound
return {'order': order}

Categories

Resources