Appending to list replaces last item in Django middleware - python-3.x

I have a middleware I'm using to retain route history within my Django app to use with breadcrumbs, but for some reason the last item in the list keeps getting replaced rather than the item appending to the end of the list.
ROOT_ROUTE_PATH = '/labels/'
class RouteHistoryMiddleware(object):
request = None
history = None
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
self.request = request
if 'history' not in request.session:
request.session['history'] = []
self.history = request.session['history']
request.session['history'].append(request.path)
if len(self.history) == 0:
self.request.previous_route = ROOT_ROUTE_PATH
elif len(self.history) == 1:
self.request.previous_route = request.session['history'][-1]
elif len(self.history) > 1:
self.request.previous_route = request.session['history'][-2]
return self.get_response(request)
Illustration of request.session['history'] mutation with above:
Open Page A
['/page_a/']
Open Page B
['/page_a/', '/page_b/']
Open Page C
['/page_a/', '/page_c/']

Instead of appending path to session try appending to self.history then overwrite history with new array:
...
self.history = request.session['history']
self.history.append(request.path)
request.session['history'] = self.history
...
You may need to change your if/else conditions after that

The issue you're running into is that Django doesn't know you've modified the list, and so the data is getting written to the session inconsistently. From the documentation:
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.
i.e, if you modify the list without reassigning it or deleting it, then it will not know the session has been modified. Again from the documentation:
we can tell the session object explicitly that it has been modified by setting the modified attribute on the session object.
So your original code should work if you add this line after modifying the list in place:
request.session.modified = True
(Replacing the list entirely, as suggested in the other answer, also works - I'm just trying to explain why your original code didn't work).

Related

Python Django: deleting an object

I'm looking to delete an object in Django, but none of the other Stack Overflow questions fix mine. I looked at this one, but it doesn't seem to be working. My delete object code (in the views file) looks like this:
#login_required
def delete_entry(request, entry_id):
"""Delete an existing entry."""
if request.method != 'POST':
# No data submitted; create a blank form.
form = TopicForm()
else:
# POST data submitted; process data.
form = TopicForm(data=request.POST)
if form.is_valid():
new_topic = form.delete(commit=False) ### Code to delete object
new_topic.owner = request.user
new_topic.save()
return redirect('learning_logs:topics')
# Display a blank or invalid form.
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
And in URLs.py:
path('delete_entry/<int:entry_id>', views.delete_entry, name='delete_entry'),
I would like to use a Bootstrap 4 button (inside a modal) to delete the entry (so without any redirects to another confirmation page),
Unfortunately, this isn't working. I'm just getting a server error saying that NoReverseMatch at /delete_entry/6.
What does this mean?
It seems like you're trying to call the delete method on the form. If you want to delete the object, you should call it on the object:
my_object = MyModel.objects.get(id=entry_id) # Retrieve the object with the specified id
my_object.delete() # Delete it
Here is the relevant Django documentation.
I think that you're looking for a DeleteView.

SQLAlchemy `History` class - why are the `added` and `deleted` attributes a list?

I have created an event listener to capture changes to instances on one of my tables, like so:
Can someone kindly explain to me what the History class is and, more importantly, why the added and deleted attributes are lists?
#event.listens_for(Model, 'before_update')
def model_update(mapper, connection, target):
""" signal when a Model is updated """
# get state of model:
state = inspect(target)
# check for changes:
for attr in state.attrs:
# get history:
hist = attr.load_history() # <-- what is this class?
# add changes to dictionary:
old_value = hist.deleted # <-- why is this a list?
new_value = hist.added # <-- why is this a list?
For context, I am trying to capture the changes to my models and save these changes to a different table (i.e. version history). Is it okay to do something like hist.deleted.pop()? Will hist.deleted always be a list? When, if ever, will it contain more than one item?
Further details: When I update an instance like so:
instance = db.query(Model).get(...)
old_value = instance.some_field
new_value = "NEW"
instance.some_field = new_value
db.add(instance)
db.commit()
print("old_value:", old_value)
print("new_value:", new_value)
This will print:
old_value: OLD
new_value: NEW
But using history, these values will show up in a list:
old_value: ["OLD"]
new_value: ["NEW"]
What is attr.load_history() ?
Returns the current pre-flush change history for this attribute, via the History interface.
This represents a History interface. It returns the history of pre-flush changes (for the requested attribute).
Each time the session is flushed, the history for each attribute is reset. (By default, the session is automatically flushed each time a request is invoked)
What is hist.deleted and hist.added ?
Representation of changes that have occurred on an attribute.
There are 3 of them: added, unchanged and deleted:
added : the collection of elements added to the attribute
unchanged : the collection of elements that have not changed on the attribute
deleted : the collection of elements that have been deleted from the attribute
They are CONSTANTLY represented as an iterable sequence

python flask session not updating

I have a flask application and i am adding/removing content to the cart via fetch api. When i try to remove the data nothing gets removed.
#cart_bp.route('/cart/action/',methods=['POST'],strict_slashes=False)
def cart():
fetchapi = request.get_json()
if fetchapi['action'].lower() == 'add':
session['cart'].append(fetchapi['item'])
else:
session['cart'].remove(fetchapi['item'])
After I print the session in the else statement the item has been removed. However when I refresh the page or navigate to another page. The cart still contains the item that got removed from the session.
I ran into this issue (solved) too. When you're updating or removing an item from the shopping cart, you need to write that to the session. In my case, it was after I updated the quantity in the add_product dictionary (within cart_products list of dictionaries):
cart_products = [add_product if product['prod_id'] == add_product['prod_id'] else product for product in cart_products]
session["cart"] = cart_products # Without this, my dictionary was updated, but the updated quantity wasn't showing in the cart.
I don't know the name of where you'd write this to based on your question as is, but it would be something like:
session['cart'].remove(fetchapi['item'])
session["cart"] = fetchapi
Or try this after .remove() line: session.modified = True

How to use global variables in django

I use django_rest_framework. I need parse huge xml file then find some data.
Parse xml on get call is bad idea because is too long time. I try once a 3 minute parse it and save parsed object to global variable. But I'm not sure about it work correctly.
Example:
class MyView(APIView):
catalog = None
parse_time = 0
#classmethod
def get_respect_catalog(cls):
if time.time() - cls.parse_time > 300:
cls.catalog = parse_xml()
cls.parse_time = time.time()
return cls.catalog
def get(self, request):
vals = catalog.xpath('tag/text()') # here i find some tag
...
return response
I sent several requests but many time variable parse_time had value 0. As if class MyView recreate sometimes and class variables "catalog" and "parse_method" resets to init values.
I think it because uwsgi have many workers and many interprets. May be exists way for using global variables in django.
P.S. I know for my case I need use database. But I want use global vars.

Pass in two variables to **kwargs

Say I have an entry form for a GUI window that asks for a new setting and a value for that setting, which it then passes to another object that manages settings and expects **kwargs as input:
class SettingsForm(someFormClass):
def onSubmit(self):
new_setting = self.content['new setting'].get()
new_value = self.content['value'].get()
settings_instance.add_setting(new_setting=new_value)
This sets new_value to the literal string 'new_setting'. I've tried various ways to get around this, such as using a dictionary:
class SettingsForm(someFormClass):
def onSubmit(self):
new_setting = self.content['new setting'].get()
new_value = self.content['value'].get()
mydict = {}
mydict[new_setting] = new_value
settings_instance.add_setting(**mydict)
This works, but doesn't make much sense for a single pair of values... is there an obvious way that I'm missing?

Resources