Deleting a ManytoMany Relationship Data in Django Project - python-3.x

I have tried to fix this issue for a while.
I have a model where there are logs entered by users and saved in Log Model. Also there is ActiveSession Model which has a ManytoMany relationship with Log Model.
In My views I am getting the POST and saving them in the Log Model. The next step is my issue where I am trying to add unqiue Logs inside the ActiveSession Model. By unique I mean by searching a field called 'log_order' in the Log Model.
The desired outcome is to:
1: Get the POST form and save the Log details in the Log Model
2: Search if the new saved Log has been previously added the many to many relation log under Active Session Model
3: If there is no Log in the ActiveSession Model to add the new Log in the Post to the activesession
4: If there are logs previously saved in ActiveSession to search for the log_order related to these logs
5: If there is no same log_order to add the new Log in the Post to the activesession
6: If the log_order is existing inside the activesession logs to delete this specific log and to add the new Log in the Post to the activesession
Models.py:
class Log(models.Model):
...................................
log_order = models.IntegerField(validators=[MinValueValidator(1)],blank=True, null=True)
date = models.DateTimeField(...........)
class LogForm(forms.Form):
.............................
log_order = forms.IntegerField()
class ActiveSession(models.Model):
log = models.ManyToManyField(Log)
..................................
Views.py:
def addlog(request, id):
active_session = ActiveSession.objects.get(id=ActiveSession.objects.last().id)
if request.method == 'POST':
form = LogForm(request.POST)
if form.is_valid():
data = Log()
data.log_order = request.POST.get('log_order')
data.log_repetitions = form.cleaned_data['log_repetitions']
data.log_weight = form.cleaned_data['log_weight']
data.save()
if active_session.log.values():
for i in active_session.log.values():
existing = i['log_order']
new = data.log_order
if int(existing) == int(new):
active_session.delete(active_session.log)
active_session.log.add(data)
active_session.save()
else:
active_session.log.add(data)
else:
active_session.log.add(data)
I have tried to save the data and update() but nothing is changing the dictionary the same exisiting outcome is printed and the new data is not replacing the existing ones. My objective is replace and avoid duplicating.
Update: I am also thought of if the keys is i['log_order'] is equal to data.log_order to delete the whole dictionary and add the new data whichever is easier
Trial:
With the below trial I got 'ManyRelatedManager' object is not subscriptable. I am not sure if I am going the wrong direction.
if active_session.log.values():
for i in range(len(active_session.log.values())):
new = data.log_order
if active_session.log[i]['log_order'] == new:
active_session.save()
else:
active_session.log.add(data)
else:
active_session.log.add(data)
Here is the current query:
<QuerySet [{'log_order': 1}, {'log_order': 1}]>
here is the requested query:
<QuerySet [{'log_order': 1}]>

I have tried and tested your code and found some issues with it.
Below, there is the code snippet I tested and it works.
I am not completely sure what is the purpose for the code, but I think you might find the method update_or_create useful (also works on related managers such as your log). Here is a link to Django docs.
def addlog(request, id):
if request.method == 'POST':
# Moved this part to the POST section of the view. It is sufficient to simplify
# the query in this way. Also, you probably want to check if there are sessions
# in the DB. Otherwise, the code would break. This returns a 404 in case there
# are absolutely no sessions.
active_session = ActiveSession.objects.last()
if active_session is None:
return Http404()
form = LogForm(request.POST)
if form.is_valid():
data = Log()
# Use form.cleaned_data instead of log_order. Form validates the data, POST dict doesn't.
data.log_order = form.cleaned_data['log_order']
# Ignored because it's missing from the model code
# data.log_repetitions = form.cleaned_data['log_repetitions']
# data.log_weight = form.cleaned_data['log_weight']
data.save()
# Use Django ORM querysets instead of values()
# See https://www.django-antipatterns.com/antipattern/over-use-of-values.html
active_session_logs = active_session.log.filter(log_order=data.log_order)
if active_session_logs.exists():
# Suggestion: try to use update() instead of a combination of delete & add.
# It reduces two SQL queries to one.
active_session_logs.delete()
active_session.log.add(data)
# After using RelatedManager.add(), no need to call save(), the changes
# are persisted in the DB simply by using add().
# Always redirect after successfully processing the form.
return redirect(reverse('test_73764999:add-log', args=(data.id,)))
else:
# If there are errors, re-render the same form with errors
return render(request, 'test_73764999/form.html', {
'form': form, 'id': id,
})
else:
# If this is a GET request, render an empty form
form = LogForm()
return render(request, 'test_73764999/form.html', {
'form': form, 'id': id,
})

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.

Appending to list replaces last item in Django middleware

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).

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

Sorting and Deleting a 1-to-Many relatiionship in SQLALCHEMY and flask

I am working a CRUD flask project that works with a 1-to-many relationship. The end result looks like this, Flask Webform. However when I submit the form for the Update route, it returns to the home page and displays this. Webform After Update.
Here is my DB model
def update(id):
frank = Frankendama.query.get(id)
form = FrankForm()
if form.validate_on_submit():
frank.title=form.title.data
frank.description=form.description.data
frank.tama=form.tama.data
frank.sarado=form.sarado.data
frank.sword=form.sword.data
frank.string=form.string.data
frank.bearing=form.bearing.data
db.session.add(frank)
db.session.commit()
comps = form.companies.data
comps_used = comps.split(",")
all_comps = Company.query.all().filter_by(Company.frankendama_id == id) #Error here
for entry in all_comps:
if entry.frankendama_id == id:
db.session.delete(entry)
for i in range(0, len(comps_used)):
new_entry = comps_used[i]
new_comp = Company(name=new_entry, frankendama_id=id)
db.session.add(new_comp)
db.session.commit()
return redirect(url_for("home"))
else:
return render_template("create.html", form=form)
I am trying to find a query for the Companies table that sorts for all rows with the foreign key 'frankendama_id' that is same to the main tables id. That way i can delete them and then re add them.
When i try using filter() or filter_by() i get the error AttributeError: 'list' object has no attribute 'filter_by'
I am really stumped, any suggestions are welcome! Thanks
move filter_by in front of .all()
all_comps = Company.query.filter_by(frankendama_id = id).all()
edit 1:
Also you can remove 'Company.' when using filter_by and you only need a single equals sign

Pyramid Hybrid application (traversal + url dispatch)

I've started using pyramid recently and I have some best-practice/general-concept questions about it's hybrid application approach. Let me try to mock a small but complex scenario for what I'm trying to do...
scenario
Say I'm building a website about movies, a place where users can go to add movies, like them and do other stuff with them. Here's a desired functionality:
/ renders templates/root.html
/movies renders templates/default.html, shows a list of movies
/movies/123456 shows details about a movie
/movies/123456/users shows list of users that edited this movie
/users renders templates/default.html, shows a list of users
/users/654321 shows details about a user
/users/654321/movies shows a list of favorite movies for this user
/reviews/movies renders templates/reviews.html, shows url_fetched movie reviews
/admin place where you can log in and change stuff
/admin/movies renders templates/admin.html, shows a list of editable movies
/admin/movies/123456 renders templates/form.html, edit this movie
extras:
ability to handle nested resources, for example movies/123/similar/234/similar/345, where similar is a property of movie class that lists ids of related movies. Or maybe more clear example would be: companies/123/partners/234/clients/345...
/movies/123456.json details about a movie in JSON format
/movies/123456.xml details about a movie in XML format
RESTFull methods (GET, PUT, DELETE..) with authorization headers for resource handling
approach
This is what I've done so far (my views are class based, for simplicity, I'll list just decorators...):
# resources.py
class Root(object):
__name__ = __parent__ = None
def __getitem__(self, name):
return None
def factory(request):
# pseudo code...
# checks for database model that's also a resource
if ModelResource:
return ModelResource()
return Root()
# main.py
def notfound(request):
return HTTPNotFound()
def wsgi_app():
config = Configurator()
config.add_translation_dirs('locale',)
config.add_renderer('.html', Jinja2Renderer)
# Static
config.add_static_view('static', 'static')
# Root
config.add_route('root', '/')
# Admin
config.add_route('admin', '/admin/*traverse', factory='factory')
config.add_route('default', '/{path}/*traverse', factory='factory')
config.add_notfound_view(notfound, append_slash=True)
config.scan('views')
return config.make_wsgi_app()
# views/root.py
#view_config(route_name='root', renderer="root.html")
def __call__():
# views/default.py
#view_defaults(route_name='default')
class DefaultView(BaseView):
#view_config(context=ModelResource, renderer="default.html")
def with_context():
#view_config()
def __call__():
# views/admin.py
#view_defaults(route_name='admin')
class AdminView(BaseView):
#view_config(context=ModelResource, renderer="admin.html")
def default(self):
#view_config(renderer="admin.html")
def __call__(self):
And following piece of code is from my real app. ModelResource is used as context for view lookup, and this is basically the reason for this post... Since all my models (I'm working with Google App Engine datastore) have same basic functionality they extend specific superclass. My first instinct was to add traversal functionality at this level, that's why I created ModelResource (additional explanation in code comments), but I'm begining to regret it :) So I'm looking for some insight and ideas how to handle this.
class ModelResource(ndb.Model):
def __getitem__(self, name):
module = next(module for module in application.modules if module.get('class') == self.__class__.__name__)
# this can be read as: if name == 'movies' or name == 'users' or .....
if name in module.get('nodes'):
# with this setup I can set application.modules[0]['nodes'] = [u'movies', u'films', u'фильми' ]
# and use any of the defined alternatives to find a resource
# return an empty instance of the resource which can be queried in view
return self
# my models use unique numerical IDs
elif re.match(r'\d+', name):
# return instance with supplied ID
cls = self.__class__
return cls.get_by_id(name)
# this is an attempt to solve nesting issue
# if this model has repeated property it should return the class
# of the child item(s)
# for now I'm just displaying the property's value...
elif hasattr(self, name):
# TODO: ndb.KeyProperty, ndb.StructuredProperty
return getattr(self, name)
# this is an option to find resource by slug instead of ID...
elif re.match(r'\w+', name):
cls = self.__class__
instance = cls.query(cls.slug == name).get()
if instance:
return instance
raise KeyError
questions
I have basically two big questions (widh some sub-questions):
How should I handle traversal in described scenario?
Should I extend some class, or implement an interface, or something else?
Should traversal be handled at model level (movie model, or it's superclass would have __getitem__ method)? Should resource be a model instance?
What's the best practice for __getitem__ that returns collection (list) of resources? In this scenario /movies/
My ModelResource in the code above is not location aware... I'm having trouble getting __public__ and __name__ to work, probably because this is the wrong way to do traversal :) I'm guessing once I find the right way, nested resources shouldn't be a problem.
How can I switch back from traversal to url dispatching?
For the extra stuff: How can I set a different renderer to handle JSON requests? Before I added traversal I configured config.add_route('json', '/{path:.*}.json') and it worked with appropriate view, but now this route is never matched...
Same problem as above - I added a view that handles HTTP methods #view_config(request_method='GET'), but it's never matched in this configuration.
Sorry for the long post, but it's fairly complex scenario. I should probably mention that real app has 10-15 models atm, so I'm looking for solution that would handle everything in one place - I want to avoid manually setting up Resource Tree...

Resources