I'm using django-modeltranslation to translate model fields. And suppose I have the following model (where the name is translated in the DB):
class Book(models.Model):
name = models.CharField(max_length=90)
Then, in a DRF view, I have an endpoint that takes a query text and searches through the book names using this code:
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
class BookView(APIView):
def get(self, request):
q = request.get('q')
vector = SearchVector('name') # this is where the issue is
query = SearchQuery(q)
matches = Book.objects.annotate(rank=SearchRank(vector, query))\
.filter(rank__gt=0.1)\
.order_by('-rank')
# etc.
This works great when I was working with English only. But now I added a new language, and all aspects of the localisation are working fine, except this search. It's looking at the name_en field values only.
If the target language is German for example, and I explicitly change the following line from:
vector = SearchVector('name')
to:
vector = SearchVector('name_de')
Then the search works over the correct field. Is there a way to pass in the correct field to SearchVector?
IIUC, you can just use get_language():
from django.utils.translation import get_language
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
class BookView(APIView):
def get(self, request):
q = request.get('q')
vector = SearchVector(f'name_{get_language()}')
query = SearchQuery(q)
matches = Book.objects.annotate(rank=SearchRank(vector, query))\
.filter(rank__gt=0.1)\
.order_by('-rank')
There are two models .I want to make query to extract only the app exact app related Adspaces .
models.py
class Appname(models.Model):
user=models.ForeignKey(User,related_name='appname', null=True, default=None,on_delete=models.CASCADE)
name=models.CharField(max_length=150,blank=False,null=False,help_text='Add your new App')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("dashapp:space",kwargs={'pk':self.pk})
class Adspace(models.Model):
user=models.ForeignKey(User,related_name='adspace', null=True, default=None,on_delete=models.CASCADE)
ad_space=models.CharField(max_length=150,blank=False,null=False)
app=models.ForeignKey('Appname', related_name='appnames',default=None, on_delete=models.CASCADE)
PID_TYPE = (
('FN','FORMAT_NATIVE'),
('FNB','FORMAT_NATIVE_BANNER'),
('FI','FORMAT_INTERSTITIAL'),
('FB','FORMAT_BANNER'),
('FMR','FORMAT_MEDIUM,RECT'),
('FRV','FORMAT_REWARDED_VIDEO'),
)
format_type=models.CharField(max_length=3,choices=PID_TYPE,default='FN',blank=False, null=False)
def __str__(self):
return self.ad_space
def get_absolute_url(self):
return reverse("dashapp:create",kwargs={'pk':self.pk})
Views.py
SHowing the one where i need to the query
class spacelist(LoginRequiredMixin,ListView):
model=Adspace
template_name='adspace_list.html'
def get_queryset(self):
query_set=super().get_queryset()
return query_set.filter(user=self.request.user)
Here I need to perform One more query so that EACH APP show their own adspaces when clicked right now every app show every show adspaces.
I have the idea what to do as if i compare app_id then it'll show the exact app related adspaces, but i dont know how to write query for the same as i already have one query present.???
You could try using a Q objects: https://docs.djangoproject.com/en/2.1/topics/db/queries/#complex-lookups-with-q-objects
From what I understand you are trying to filter both on the app_id and the request user at the same time, so you could try look something like this:
from django.db.models import Q
...
def get_queryset(self):
query_set=super().get_queryset()
return query_set.filter(Q(user=self.request.user) & Q(app_id=app_id))
...
This lets you do a single filter with both your requirements at the same time (i.e. retrieve the Adspace instances for a specific user with a specific Appname).
You chain another filter at the end like this:
class spacelist(LoginRequiredMixin,ListView):
model=Adspace
template_name='adspace_list.html'
def get_queryset(self):
query_set = super().get_queryset()
query_set = query_set.filter(user=self.request.user)
app_id = [...]
return query_set.filter(app_id=app_id)
The problem left is to find out what is the app_id coming from. How do you know what is the current app? Several options here.
Option 1: From the request
It can come from the current user: self.request.user.appname.all() but that will give you multiple apps, if the user can only have one app, you should change your model Appname.user to a OneToOneField.
Otherwise, I suggest changing your related_name='appnames' to reflect the multiplicity in the reverse relationship.
Option 2: From the URL
It can come from the URL, your space list view should extract an app_id parameter from the URL where it's defined:
url(r'^(?P<app_id>[0-9]+)/spaces/$', spacelist.as_view(), name='space_list'),
And then in the spacelist view, you would get this parameter like this:
app_id = self.kwargs['app_id']
return query_set.filter(app_id=app_id)
Hope that helps
UPDATE:
Also worth noting that QuerySets are lazy, meaning the result will get evaluated as late as possible by Django. Therefore, when you call:
query_set = query_set.filter(user=self.request.user)
The Django ORM doesn't execute any DB queries yet, and you can chain more filters after that:
query_set = query_set.filter(user=self.request.user)
query_set = query_set.filter(app_id=app_id)
Which behind the scenes is extending the query that will be executed when required. But at this point, no query is actually run. To see the query that will get executed you can print out the query attribute of the QuerySet:
print(query_set.query)
Which should log something like:
SELECT "app_adspace"."user_id" ...
FROM
"app_adspace"
WHERE
"app_adspace"."user_id" = 1234 AND "app_adspace"."app_id" = 5678
For the following API endpoint,
import json
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpResponseBadRequest
from django.views.decorators.http import require_POST
from lucy_web.models import UserApn
#login_required
#require_POST
def save_apn(request, version):
player_id = json.loads(request.body).get('player_id')
if player_id:
UserApn.objects.get_or_create(user=request.user, player_id=player_id)
return JsonResponse({'status': 'success'})
else:
return HttpResponseBadRequest()
Here is the underlying model:
from django.contrib.auth.models import User
from django.db import models
from .timestamped_model import TimeStampedModel
class UserApn(TimeStampedModel):
user = models.ForeignKey(User)
player_id = models.CharField(max_length=255)
The call to get_or_create() has been raising some MultipleObjectsReturned errors. To fix this, I'd like to impose a unique_together constraint on the user and player_id. Firstly, however, I have to write a data migration that eliminates rows that violate this unique together constraint.
How could I write a query that selects these? Right now the following has been proposed:
def remove_duplicate_apns(apps, schema_editor):
UserApn = apps.get_model('lucy_web', 'UserApn')
previous_user_id = None
previous_player_id = None
for apn in UserApn.objects.all().order_by('user_id', 'player_id'):
if apn.user_id == previous_user_id and apn.player_id == previous_player_id:
print(f'deleting {apn} (id: {apn.id})')
apn.delete()
else:
previous_user_id = apn.user_id
previous_player_id = apn.player_id
It seems, though, that this could also be done in a single query.
Update
I've found that one can pass the two fields, user and player_id, to .values(), and then check for duplicates using .distinct(). For example, the following test passes:
from django.test import TestCase
from django.contrib.auth.models import User
from myapp.models import UserApn
class UserApnTest(TestCase):
def test_1(self):
user = User.objects.create_user(username='jayz')
apn1 = UserApn.objects.create(user=user, player_id='foo')
apn2 = UserApn.objects.create(user=user, player_id='foo')
apn3 = UserApn.objects.create(user=user, player_id='bar')
self.assertEqual(
len(UserApn.objects.values('user', 'player_id')) -
len(UserApn.objects.values('user', 'player_id').distinct()), 1)
The problem remains, however, that the output from this is dictionaries with user_id and player_id, but the original id is lost, so I can't subsequently get() the duplicate objects and delete them. How can I do something similar but retain references to the duplicate objects?
I managed to group the duplicate UserApns into the following queryset:
UserApn.objects.all().difference(UserApn.objects.distinct('user', 'player_id'))
Note that passing multiple arguments to distinct() only works on PostgreSQL.
Here is what I want to do:
Retrieve a list of all objects in all my django apps that inherit from forma.Form
Find all CharField defined on the form
Verify that each of these fields has a max_length kwarg specified
My goal is the write a unit test to fail if a form exists within our system that does not have a max length specified.
how would I do this?
I found a way to use reflection style code to retrieve all of the objects inheriting from the models.Form class, see test below:
class TestAllFormsMeetBasicBoundaryTests(TestCase):
def all_subclasses(self, cls):
return cls.__subclasses__() + [g for s in cls.__subclasses__()
for g in self.all_subclasses(s)]
# this is teh awesome-sause! dynamically finding objects in the project FTW!
def test_all_char_fields_contain_max_length_value(self):
from django.apps import apps
import django.forms
import importlib
log.info('loading all forms modules from apps')
at_least_one_module_found = False
for app_name in settings.PROJECT_APPS: #this is a list of in project apps
app = apps.get_app_config(app_name)
try:
importlib.import_module('{0}.forms'.format(app.label))
log.info('forms loaded from {0}'.format(app.label))
at_least_one_module_found = True
except ImportError as e:
pass
self.assertTrue(at_least_one_module_found, 'No forms modules were found in any of the apps. this should never be true!')
all_forms = self.all_subclasses(django.forms.Form)
messages = []
for form in all_forms:
for key in form.declared_fields.keys():
field = form.declared_fields[key]
if isinstance(field, django.forms.CharField) and form.__module__.partition('.')[0] in settings.PROJECT_APPS and field.max_length is None:
messages.append('{0}.{1}.{2} is missing a max_length value.'.format(form.__module__, form.__name__, key))
pass
self.assertEquals(0, len(messages),
'CharFields must always have a max_length attribute specified. please correct the following:\n--{0}'
.format('\n--'.join(messages)))
I'm writing a Django app, and I need a function to update a field in the database. Is there any reason to do one of these methods rather than the other?
def save_db_field(name,field,value):
obj = MyModel.objects.get(name=name)
obj.field = value
obj.save()
def update_db_field(name,field,value):
MyModel.objects.get(name=name).update(field=value)
It seems like the second is better because it does it in one DB call instead of two. Is there a reason why fetching, then updating is any better?
There are several key differences.
update is used on a queryset, so it is possible to update multiple objects at once.
As #FallenAngel pointed out, there are differences in how custom save() method triggers, but it is also important to keep in mind signals and ModelManagers. I have build a small testing app to show some valuable differencies. I am using Python 2.7.5, Django==1.7.7 and SQLite, note that the final SQLs may vary on different versions of Django and different database engines.
Ok, here's the example code.
models.py:
from __future__ import print_function
from django.db import models
from django.db.models import signals
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
__author__ = 'sobolevn'
class CustomManager(models.Manager):
def get_queryset(self):
super_query = super(models.Manager, self).get_queryset()
print('Manager is called', super_query)
return super_query
class ExtraObject(models.Model):
name = models.CharField(max_length=30)
def __unicode__(self):
return self.name
class TestModel(models.Model):
name = models.CharField(max_length=30)
key = models.ForeignKey('ExtraObject')
many = models.ManyToManyField('ExtraObject', related_name='extras')
objects = CustomManager()
def save(self, *args, **kwargs):
print('save() is called.')
super(TestModel, self).save(*args, **kwargs)
def __unicode__(self):
# Never do such things (access by foreing key) in real life,
# because it hits the database.
return u'{} {} {}'.format(self.name, self.key.name, self.many.count())
#receiver(pre_save, sender=TestModel)
#receiver(post_save, sender=TestModel)
def reicever(*args, **kwargs):
print('signal dispatched')
views.py:
def index(request):
if request and request.method == 'GET':
from models import ExtraObject, TestModel
# Create exmple data if table is empty:
if TestModel.objects.count() == 0:
for i in range(15):
extra = ExtraObject.objects.create(name=str(i))
test = TestModel.objects.create(key=extra, name='test_%d' % i)
test.many.add(test)
print test
to_edit = TestModel.objects.get(id=1)
to_edit.name = 'edited_test'
to_edit.key = ExtraObject.objects.create(name='new_for')
to_edit.save()
new_key = ExtraObject.objects.create(name='new_for_update')
to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key)
# return any kind of HttpResponse
That resuled in these SQL queries:
# to_edit = TestModel.objects.get(id=1):
QUERY = u'SELECT "main_testmodel"."id", "main_testmodel"."name", "main_testmodel"."key_id"
FROM "main_testmodel"
WHERE "main_testmodel"."id" = %s LIMIT 21'
- PARAMS = (u'1',)
# to_edit.save():
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'edited_test'", u'2', u'1')
# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'updated_name'", u'3', u'2')
We have just one query for update() and two for save().
Next, lets talk about overriding save() method. It is called only once for save() method obviously. It is worth mentioning, that .objects.create() also calls save() method.
But update() does not call save() on models. And if no save() method is called for update(), so the signals are not triggered either. Output:
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
# TestModel.objects.get(id=1):
Manager is called [<TestModel: edited_test new_for 0>]
Manager is called [<TestModel: edited_test new_for 0>]
save() is called.
signal dispatched
signal dispatched
# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
Manager is called [<TestModel: edited_test new_for 0>]
As you can see save() triggers Manager's get_queryset() twice. When update() only once.
Resolution. If you need to "silently" update your values, without save() been called - use update. Usecases: last_seen user's field. When you need to update your model properly use save().
Both looks similar, but there are some key points:
save() will trigger any overridden Model.save() method, but update() will not trigger this and make a direct update on the database level. So if you have some models with overridden save methods, you must either avoid using update or find another way to do whatever you are doing on that overridden save() methods.
obj.save() may have some side effects if you are not careful. You retrieve the object with get(...) and all model field values are passed to your obj. When you call obj.save(), django will save the current object state to record. So if some changes happens between get() and save() by some other process, then those changes will be lost. use save(update_fields=[.....]) for avoiding such problems.
Before Django version 1.5, Django was executing a SELECT before INSERT/UPDATE, so it costs 2 query execution. With version 1.5, that method is deprecated.
In here, there is a good guide or save() and update() methods and how they are executed.
save() method can be used to insert new record and update existing record and generally used for saving instance of single record(row in mysql) in database.
update() is not used to insert records and can be used to update multiple records(rows in mysql) in database.
Using update directly is more efficient and could also prevent integrity problems.
From the official documentation https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query.QuerySet.update
If you’re just updating a record and don’t need to do anything with
the model object, the most efficient approach is to call update(),
rather than loading the model object into memory. For example, instead
of doing this:
e = Entry.objects.get(id=10)
e.comments_on = False
e.save()
…do this:
Entry.objects.filter(id=10).update(comments_on=False)
Using update() also prevents a race condition wherein something might
change in your database in the short period of time between loading
the object and calling save().
Update only works on updating querysets. If you want to update multiple fields at the same time, say from a dict for a single object instance you can do something like:
obj.__dict__.update(your_dict)
obj.save()
Bear in mind that your dictionary will have to contain the correct mapping where the keys need to be your field names and the values the values you want to insert.
Update will give you better performance with a queryset of more than one object, as it will make one database call per queryset.
However save is useful, as it is easy to override the save method in your model and add extra logic there. In my own application for example, I update a dates when other fields are changed.
Class myModel(models.Model):
name = models.CharField()
date_created = models.DateField()
def save(self):
if not self.pk :
### we have a newly created object, as the db id is not set
self.date_created = datetime.datetime.now()
super(myModel , self).save()
Use _state.adding to differentiate update from create https://docs.djangoproject.com/en/3.2/ref/models/instances/
def save(self, *args, **kwargs):
# Check how the current values differ from ._loaded_values. For example,
# prevent changing the creator_id of the model. (This example doesn't
# support cases where 'creator_id' is deferred).
if not self._state.adding and (
self.creator_id != self._loaded_values['creator_id']):
raise ValueError("Updating the value of creator isn't allowed")
super().save(*args, **kwargs)
one of the differences that might cause a lot of headaches is that save updates, but update does NOT update columns of type DateTimeField(auto_now=True)
or ModificationDateTimeField
These fields automatically (should) set their date when an object is saved to the database.
save():
can be used with model object but not with QuerySet or Manager objects.
with select_for_update() can run SELECT FOR UPDATE query.
update()
can be used with QuerySet or Manager objects but not with model object.
with select_for_update() cannot run SELECT FOR UPDATE query.
For example, I have Person model as shown below:
# "store/models.py"
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
Then, you can use save() with Person model object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.get(id=1)
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1).first()
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
But, you cannot use save() with QuerySet object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1)
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, the error below occurs:
AttributeError: 'QuerySet' object has no attribute 'save'
And, you cannot use save() with Manager object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, the error below occurs:
AttributeError: 'Manager' object has no attribute 'save'
Then, you can use update() with QuerySet object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1)
person.update(name="Tom") # Here
return HttpResponse("Test")
And, you can use update() with Manager object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects
person.update(name="Tom") # Here
return HttpResponse("Test")
But, you cannot use update() with Person model object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.get(id=1)
person.update(name="Tom") # Here
return HttpResponse("Test")
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1).first()
person.update(name="Tom") # Here
return HttpResponse("Test")
Then, the error below occurs:
AttributeError: 'Person' object has no attribute 'update'
Next for example, select_for_update() is used to prevent race condition(lost update or write skew) when updating data in Django.
And, I have test view with save() and select_for_update().filter(pk=1).first() as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update().filter(pk=1).first() # Here
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE and UPDATE queries are run as shown below. *I used PostgreSQL and these logs below are the query logs of PostgreSQL and you can check On PostgreSQL, how to log SQL queries with transaction queries such as "BEGIN" and "COMMIT":
Now, I remove first() to use update() as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update().filter(pk=1) # Here
person.update(name="Tom") # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE query is not run and only UPDATE query is run as shown below:
And, I have test view with save() and select_for_update().get(pk=1) as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update().get(pk=1) # Here
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE and UPDATE queries are run as shown below:
Now, I remove get() to use update() as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update() # Here
person.update(name="Tom") # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE query is not run and only UPDATE query is run as shown below:
So, save() with select_for_update() can run SELECT FOR UPDATE query while update() with select_for_update() cannot.