I'm trying to create a view function which updates the users cart (session) on add and removal.
def shoppingCartAdd(request):
data = json.loads(request.POST['post_data'])
if 'shopping_cart' not in request.session: #<-- create the session dict
request.session['shopping_cart'] = {}
if data["action"] == "add":
with open(MEDIA_ROOT + '/json/products.js', 'r') as json_file:
products = json.loads(json_file.read()) #<-- json file that contains product information
json_file.close()
item = products["products"][data["id"]] #<-- get the item info from json
#If the item is not in the session add it. Otherwise do something.
if data["id"] not in request.session['shopping_cart']:
request.session['shopping_cart'][data["id"]] = item
else:
print('Exists')
#Do something else.
#Remove the item from the dict.
if data["action"] == "remove":
request.session['shopping_cart'].pop([data["id"]], None)
context = {'shoppingCart' : request.session['shopping_cart']}
return JsonResponse(context)
My problem is that I cannot add more than two items to my dictinary. I'm not sure what I'm doing wrong here. If I click on the first item it'll create the session and add it properly, and if I try to add it again it'll print out "exists". But if I add a second and try to add it again, it will not print out "exists" on the 2nd item.
Here's a print of my session with two items. using print(request.session['shopping_cart'])
{
'38': {'name': 'hergh', 'price': 23, 'active': 'active', 'pk': 38, 'imageURL': '/media/gg_faUCQOg.jpg', 'type': 'sokker', 'amount': 13},
'39': {'name': 'dea', 'price': 1, 'active': 'active', 'pk': 39, 'imageURL': '/media/gg_6ECtbKE.jpg', 'type': 'sokker', 'amount': 1}
}
According to the documentation - https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved
By default, Django only saves to the session database when the session has been modified – that is if any of its dictionary values have been assigned or deleted.
You can set the SESSION_SAVE_EVERY_REQUEST setting to True, this will force save the session on every request.
Related
I'm trying to get particular values out of a large dictionary and I'm having trouble doing so. I'm parsing through data from an API and attempting to get just the name attribute from my response. This is the format of the response I'm getting back:
{'data':
[{'id': '5555', 'type': 'proj-pha', 'links': {'self': '{sensitive_url}'}, 'attributes':
{'active': True, 'name': 'Plan', 'language': 'plan_l', 'position': 1},
'relationships': {'account': {'links': {'self': '{sensitive_url}', 'related':
'{sensitive_url}'}}, 'pro-exp': {'links': {'self':
'{sensitive_url}', 'related': '{sensitive_url}'}}}}
To clarify, I'm printing out the API response as a dictionary using:
print(response.json())
Here is some general code from my script for context:
params = {
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
"response_type": RESPONSE_TYPE
}
token = '{sensitive}'
print("Bearer Token: " + token)
session = requests.session()
session.headers = {"authorization": f"Bearer {token}"}
base_api_endpoint = "{sensitive}"
response = session.get(base_api_endpoint)
print(response.json())
What I want is just the 'name': 'Plan' attribute and that's all. The data provided repeats itself to the next "id" and so on until all the iterations have been posted. I'm trying to query out a list of all of these. I'm not looking for a particular answer on how to loop through these to get all of them though that would be helpful, I'm more focused on being able to just pick out the "name" value by itself.
Thanks!
To get all the names, just use list comprenhension, like this:
[item['attributes']['name'] for item in response['data']]
If you only want the name of the i-th item, just do:
response['data'][i]['attributes']['name']
And the last, if you want the name for a specific id:
def name_by_id(response, id):
for item in response['data']:
if item['id'] == id:
return item['attributes']['name']
return None
I have a problem I would like to loop inside Stripe to create dynamic multiple objects in checkout. I can not do that after stripe.checkout.Session.create() because I get an error. Also I can not create JSON object in for loop out of stripe.checkout.Session.create(). Any ideas? How can I use for loop and create multiple line_items?
def create_checkout_session(request):
if request.method == "GET":
try:
cart = Cart.objects.get(order_user=request.user)
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card', 'p24'],
line_items=[{
'price_data': {
'currency': 'eur',
'product_data': {
'name': 'total'
},
'unit_amount': cart.total,
},
'quantity': 1,
}],
You should be able to iterate to prepare line_items according to your needs then pass the prepared array:
count = 5
lineItems = []
for i in range(count):
lineItems.append({...})
checkout_session = stripe.checkout.Session.create(
line_items=**lineItems**,
...
)
I'm using the visjs plugin to create a timeline on my page. The timeline code requires me to give it multiple dicts in a list which look like this:
var items = new vis.DataSet([
{id: 1, content: 'item 1', start: '2013-04-20'},
{id: 2, content: 'item 2', start: '2013-04-14'},
{id: 3, content: 'item 3', start: '2013-04-16', end: '2013-04-19'},
]);
Now I'm trying to create something like this for my Index View by using a for loop and adding my dicts to a list, which I then put in my context and use it in my template. My code looks like this:
class ItemIndex(SingleTableMixin, FilterView):
model = Item
context_object_name = 'items'
template_name = 'accounting/item/item_index.html'
def get_context_data(self, **kwargs):
items = Item.objects.all()
context = super().get_context_data(**kwargs)
dict_list = []
for item in items:
dict = {
"id": item.pk,
"content": Item (# {item.pk})',
"start": str(item.start), #DateField
"end": str(item.end), #DateField
}
dict_list.append(dict)
context.update({
"dict_list": dict_list,
})
return context
Now I don't find this is the prettiest solution, but I don't know anything better (maybe someone has a better idea).
In my html I'm calling the dict_list like this:
var items = new vis.DataSet(
"{{dict_list}}"
);
My problem is that the list I'm getting back looks like this:
[{'id': 3, 'content': 'Item (# 3)', 'start': '2020-01-01', 'end': '2020-01-03'}, {'id': 4, 'content': 'Item (# 4)', 'start': '2020-01-01', 'end': 'None'}]
My problem is that the quotes around the keys "destroy" the whole list and thus my timeline isn't shown properly.
Is there a good solution to somehow create the dicts without the quotes around the keys?
I'm using Python 3 and Django 2.2.
Thanks for any help!
You here have a Python object, and by using {{ dict_list }} you get a HTML escaped version of the str(..) of that object. You probably better use JSON as an intermediate format. You thus first JSON-encode the object in the context:
context.update(
dict_list=json.dumps(dict_list),
)
At the template side, you can then parse the JSON back. You should mark the dict_list variable as safe, to ensure that it does not HTML-escape it:
var items = new vis.DataSet(
JSON.parse('{{ dict_list|safe }}')
);
Hopefully a pretty easy questions follows. When I get an item with Pyrebase's .get() method, like so:
for company_id in game[company_type]:
pyre_company = db.child("companies/data").order_by_child("id").equal_to(company_id).limit_to_first(
1).get()
company = pyre_company.val()
print(company)
break # Run only once for testing purposes
I get this following output, even though I use the .val()
OrderedDict([('-LEw2zHYiJ6p15iBhKuZ', {'id': 427, 'name': 'Bugbear Entertainment', 'type': 'developer'})])
But I only want the JSON Object
{'id': 427, 'name': 'Bugbear Entertainment', 'type': 'developer'}
This is because
db.child("companies/data").order_by_child("id").equal_to(company_id).limit_to_first(1).get()
is a Query, because you call the orderByChild() method on a Reference (as well as an equalTo() method btw).
As explained here in the JavaScript SDK doc:
Even when there is only a single match for the query, the snapshot is still a
list; it just contains a single item. To access the item,
you need to loop over the result:
ref.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
// ...
});
});
With pyrebase you should use the each() method, as explained here, which "Returns a list of objects on each of which you can call val() and key()".
pyre_company = db.child("companies/data").order_by_child("id").equal_to(company_id).limit_to_first(1).get()
for company in pyre_company.each():
print(company.val()) // {'id': 427, 'name': 'Bugbear Entertainment', 'type': 'developer'}
In Python Eve framework, is it possible to have a condition which checks combination of two fields to be unique?
For example the below definition restricts only firstname and lastname to be unique for items in the resource.
people = {
# 'title' tag used in item links.
'item_title': 'person',
'schema': {
'firstname': {
'type': 'string',
'required': True,
'unique': True
},
'lastname': {
'type': 'string',
'required': True,
'unique': True
}
}
Instead, is there a way to restrict firstname and lastname combination to be unique?
Or is there a way to implement a CustomValidator for this?
You can probably achieve what you want by overloading the _validate_unique and implementing custom logic there, taking advantage of self.document in order to retrieve the other field value.
However, since _validate_unique is called for every unique field, you would end up performing your custom validation twice, once for firstname and then for lastname. Not really desirable. Of course the wasy way out is setting up fullname field, but I guess that's not an option in your case.
Have you considered going for a slighty different design? Something like:
{'name': {'first': 'John', 'last': 'Doe'}}
Then all you need is make sure that name is required and unique:
{
'name': {
'type':'dict',
'required': True,
'unique': True,
'schema': {
'first': {'type': 'string'},
'last': {'type': 'string'}
}
}
}
Inspired by Nicola and _validate_unique.
from eve.io.mongo import Validator
from eve.utils import config
from flask import current_app as app
class ExtendedValidator(Validator):
def _validate_unique_combination(self, unique_combination, field, value):
""" {'type': 'list'} """
self._is_combination_unique(unique_combination, field, value, {})
def _is_combination_unique(self, unique_combination, field, value, query):
""" Test if the value combination is unique.
"""
if unique_combination:
query = {k: self.document[k] for k in unique_combination}
query[field] = value
resource_config = config.DOMAIN[self.resource]
# exclude soft deleted documents if applicable
if resource_config['soft_delete']:
query[config.DELETED] = {'$ne': True}
if self.document_id:
id_field = resource_config['id_field']
query[id_field] = {'$ne': self.document_id}
datasource, _, _, _ = app.data.datasource(self.resource)
if app.data.driver.db[datasource].find_one(query):
key_names = ', '.join([k for k in query])
self._error(field, "value combination of '%s' is not unique" % key_names)
The way I solved this issue is by creating a dynamic field using a combination of functions and lambdas to create a hash that will use
which ever fields you provide
def unique_record(fields):
def is_lambda(field):
# Test if a variable is a lambda
return callable(field) and field.__name__ == "<lambda>"
def default_setter(doc):
# Generate the composite list
r = [
str(field(doc)
# Check is lambda
if is_lambda(field)
# jmespath is not required, but it enables using nested doc values
else jmespath.search(field, doc))
for field in fields
]
# Generate MD5 has from composite string (Keep it clean)
return hashlib.md5(''.join(r).encode()).hexdigest()
return {
'type': 'string',
'unique': True,
'default_setter': default_setter
}
Practical Implementation
My use case was to create a collection that limits the amount of key value pairs a user can create within the collection
domain = {
'schema': {
'key': {
'type': 'string',
'minlength': 1,
'maxlength': 25,
'required': True,
},
'value': {
'type': 'string',
'minlength': 1,
'required': True
},
'hash': unique_record([
'key',
lambda doc: request.USER['_id']
]),
'user': {
'type': 'objectid',
'default_setter': lambda doc: request.USER['_id'] # User tenant ID
}
}
}
}
The function will receive a list of either string or lambda function for dynamic value setting at request time, in my case the user's "_id"
The function supports the use of JSON query with the JMESPATH package, this isn't mandatory, but leave the door open for nested doc flexibility in other usecases
NOTE: This will only work with values that are set by the USER at request time or injected into the request body using the pre_GET trigger pattern, like the USER object I inject in the pre_GET trigger which represents the USER currently making the request