Django - storing queryset in request.session still queries the db - why? - django

def mysearch(request):
"""This view builds a Q object query based on which fields are filled."""
if 'submit' in request.POST:
# build Q object depending on fields submitted
q = Q()
if request.POST['first_field']:
q &= Q(firstfield__icontains = request.POST['first_field'])
...
if request.POST['sixth_field']:
q &= Q(sixthfield__icontains = request.POST['sixth_field'])
results_list = MyModel.objects.filter(q)
count = len(results_list)
# store results
request.session['results_list'] = results_list
request.session['count'] = count
# 'p' is an arbitrary marker to detonate pagination of a page other than 1
if 'p' in request.GET:
results_list = request.session['results_list']
count = request.session['count']
if count and count > 0:
...
# pagination code
...
else:
pass
return render_to_response('search_results.html',
locals(), context_instance=RequestContext(request))
All works well in my templates using the paginator. The problem is that Django debug toolbar tells me that I am hitting the database the same number of times on pages > 1 than I am on the first page. Why is this? In fact - why is it hitting the database at all? Shouldn't the whole results_list be being pulled from request.session? Any advice much appreciated.

You're saving a queryset object in the session. Querysets are like SQL statements, but they cache the results. You haven't run the query when you put it in the session, so what you're storing is essentially just the query. When it gets pulled out it's still just a query that hasn't run, so the queryset gets run again. To ensure that you're storing just the actual results, do this:
request.session['results_list'] = list(results_list)
and to save you another query when you find count you can...
request.session['count'] = len(request.session['results_list'])
Also keep in mind that session data is (by default) saved in the database, so you might not be doing yourself any favors by storing a python pickled representation of the full data. It might in fact be faster just to go to the original table and pull it out that way.

Related

How to access stored user data in django?

I'm new to django, especially the storing of user data. Basically, I want to save user data searches. They put in a query into a search_bar and I want to save that query. I'm trying to create a list for each user with their search queries at user['search'] and the results of these queries at user['results']. However, I added some code and it does not seem to save the results.
Here is my code for user['results']. It's the same as for user['search']. I'm trying to save the query results by using request.session['result'] = query_result. The reason the results are not obvious is that they make a few choices between entering the query and seeing results.
from django.contrib.sessions.models import Session
request.session.modified = True
object_save = list(_objs.values('service__code'))
if not 'result' in request.session or not request.session['result']:
renderequest.session['result'] = [object_save]
request.session.save()
else:
result_list = request.session['result']
result_list.append(object_save)
request.session['result'] = result_list
request.session.save()
I'd expect this to be able to save and I can look at the searches in python manage.py shell.
When I try to pull all the session data with s = Session.object.get(pk='pk') and s['result'] I get nothing. s has no attribute 'result' is the error.
Maybe I'm completely not understanding user sessions, please help.

Django: Changing model attributes via shell creates an empty copy of said model, but doesn't change original [duplicate]

How do I run an update and select statements on the same queryset rather than having to do two queries:
- one to select the object
- and one to update the object
The equivalent in SQL would be something like:
update my_table set field_1 = 'some value' where pk_field = some_value
Use the queryset object update method:
MyModel.objects.filter(pk=some_value).update(field1='some value')
Django database objects use the same save() method for creating and changing objects.
obj = Product.objects.get(pk=pk)
obj.name = "some_new_value"
obj.save()
How Django knows to UPDATE vs. INSERT
If the object’s primary key attribute is set to a value that evaluates to True (i.e., a value
other than None or the empty string), Django executes an UPDATE. If
the object’s primary key attribute is not set or if the UPDATE didn’t
update anything, Django executes an INSERT.
Ref.: https://docs.djangoproject.com/en/1.9/ref/models/instances/
This answer compares the above two approaches.
If you want to update many objects in a single line, go for:
# Approach 1
MyModel.objects.filter(field1='Computer').update(field2='cool')
Otherwise you would have to iterate over the query set and update individual objects:
#Approach 2
objects = MyModel.objects.filter(field1='Computer')
for obj in objects:
obj.field2 = 'cool'
obj.save()
Approach 1 is faster because, it makes only one database query, compared to approach 2 which makes 'n+1' database queries. (For n items in the query set)
Fist approach makes one db query ie UPDATE, the second one makes two: SELECT and then UPDATE.
The tradeoff is that, suppose you have any triggers, like updating updated_on or any such related fields, it will not be triggered on direct update ie approach 1.
Approach 1 is used on a queryset, so it is possible to update multiple objects at once, not in the case of approach 2.
1st method
MyTable.objects.filter(pk=some_value).update(field1='some value')
2nd Method
q = MyModel.objects.get(pk=some_value)
q.field1 = 'some value'
q.save()
3rd method
By using get_object_or_404
q = get_object_or_404(MyModel,pk=some_value)
q.field1 = 'some value'
q.save()
4th Method
if you required if pk=some_value exist then update it other wise create new one by using update_or_create.
MyModel.objects.update_or_create(pk=some_value,defaults={'field1':'some value'})
If you need to set the new value based on the old field value that is do something like:
update my_table set field_1 = field_1 + 1 where pk_field = some_value
use query expressions:
MyModel.objects.filter(pk=some_value).update(field1=F('field1') + 1)
This will execute update atomically that is using one update request to the database without reading it first.
only in a case in serializer things, you can update in very simple way!
my_model_serializer = MyModelSerializer(
instance=my_model, data=validated_data)
if my_model_serializer.is_valid():
my_model_serializer.save()
only in a case in form things!
instance = get_object_or_404(MyModel, id=id)
form = MyForm(request.POST or None, instance=instance)
if form.is_valid():
form.save()
Accepted answer works great, but it comes with some unwanted side effect.
For example, you are using imageField, the update() will work and update others data, but not update your imageField data
class ProfileSetting(models.Model):
first_name = models.CharField(blank=True)
logo = models.ImageField(blank=True, null=True, upload_to="profile/logo/")
update_data = {
"first_name": "john",
"logo": request.FILES['logo'] # logo will not be properly update
}
ProfileSetting.objects.filter(pk=some_value).update(**update_data)
Here is some example with good explanation Django ImageField is not updating when update() method is used

Filtering out duplicate and none values from Django queryset

In a Django app, I can access user sessions and then get users_ids with flat=True. E.g. I'm doing:
Session.objects.filter(last_activity_gte=time_window).values_list('user_id',flat=True)
But the result is tainted by duplicates and None values. How do I perform the same query and filter out None or duplicates?
One way to do this is:
time_window = timezone.now() - timedelta(minutes=5)
user_ids = Session.objects.filter(last_activity_gte=time_window).values_list('user_id',flat=True)
user_ids = [id for id in user_ids if id is not None]
user_ids = set(user_ids)
But I wonder if I could have achieved that directly while querying the DB, which would be faster. Performance is crucial.
If anyone's interested, I'm using https://github.com/Bouke/django-user-sessions to be able to access Django session objects as ORM objects
To exclude empty user fields, filter by isnull.
(Session.objects
.filter(user__isnull=False)
.filter(last_activity_gte=time_window)
.values_list('user_id', flat=True))
To remove duplicates, you can use .distinct('user'), but that works only on some database back ends. MySQL doesn't seem to support it.

Django request.session does not resolve

I have a ManyToMany relation between 2 of my models in the same app. This looks like following:
class Event(models.Model):
eventID = models.CharField(deafult = random_eventID)
signal = models.ManyToManyField(Signal)
....
....
Now, in my Form (using ModelForm) my eventID field is already populated with the eventID every time i refresh the page (because it gets a new random_eventID every time i refresh the page).
This way when, in my forms i select to add a new signal (I want to be able to add signals when i create an event)...it goes to a different view. I save the event and when i return back to the Event page, the eventID is changed again. I want to have all the data which the user has already filled/selected in the form to be present when it returns back to the Event page after adding lots of different stuff.
Solutions i thought of :
1 - I cannot make changes to my model so as to include another column and save the Event before going to another page & later retrieve it back.
2 - Using sessions i save all the data already present in all the fields an later retrieve it back..( This way HTTP is no more stateless, but it serves my purpose.)
3 - Will Ajax help in doing any update (which i don't understand it will)
I tried it using session and came cross this silly error, which i am not able to resolve.
views.py
def create(request):
if request.POST:
form = EventForm(request.POST)
form .save()
del request.session['event_id']
return HttpResponseRedirect('/Event')
else:
event_session = request.session.get('event_id')
if event_session is not None:
form = EiEventForm(initial={'eventID' : event_session}
else:
form = EventForm()
request.session['event_id'] = form('eventID').value()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('event.html', args)
With the above, after debugging i do not get the current value in the eventID field..I tried some other ways as well but with no success.
The request.GET.get('eventID') returns None..How can i get the values from my field ?
Also, is there a better way to accomplish the desired result except sessions.
Any help would be great help!

Django formset if TOTAL_FORMS has different number than actual number of forms

When dealing with dynamic formset, there are times when TOTAL_FORMS is greater than the actual number of forms. Also, this input TOTAL_FORMS can be modified easily by a user.
So for example, my input is
<input name='user-TOTAL_FORMS' type='hidden' value='5'/>
However, there are only 2 actual forms displayed.
In this case Django generates unwanted empty forms in formset.forms variable. And this creates a problem if there are any validation errors and form is displayed again. Page shows those unwanted forms. (In the example, only 2 actual forms should be displayed but since total count is 5, a user sees total 5 forms)
How can I remove these unwanted forms, update my total count and redisplay the forms with updated formset?
EDIT:
The challenging part is to update the indices as well when removing forms. So the total forms count match with the last form index.
It's an old question and I am not sure if Django has changed much since. But the way I ended up doing it was to write a function to update formset data. The key here is to make a copy of the formset data (QueryDict) first. Here is the code:
def updateFormDataPrefixes(formset):
"""
Update the data of the formset to fix the indices. This will assign
indices to start from 0 to the length. To do this requires copying
the existing data and update the keys of the QueryDict.
"""
# Copy the current data first
data = formset.data.copy()
i = 0
nums = []
for form in formset.forms:
num = form.prefix.split('-')[-1]
nums.append(num)
# Find the keys for this form
matched_keys = [key for key in data if key.startswith(form.prefix)]
for key in matched_keys:
new_key = key.replace(num, '%d'%i)
# If same key just move to the next form
if new_key == key:
break
# Update the form key with the proper index
data[new_key] = data[key]
# Remove data with old key
del data[key]
# Update form data with the new key for this form
form.data = data
form.prefix = form.prefix.replace(num, '%d'%i)
i += 1
total_forms_key = formset.add_prefix(TOTAL_FORM_COUNT)
data[total_forms_key] = len(formset.forms)
formset.data = data
Lol, It still old question, but real answer is "add extra=0 attribute, because default extra definition is 3"
LinkFormSet = inlineformset_factory(
ParentModel,
ChildModel,
fields = ('price', 'deadline',),
extra=0
)
More documentation is available here : https://docs.djangoproject.com/en/2.1/ref/forms/models/#django.forms.models.inlineformset_factory