My model in django has a character field with maximum length 500 character. I'm working with a PostgreSQL database:
class Chat(models.Model):
message = models.CharField(max_length=500)
username = models.CharField(max_length=255)
date = models.DateTimeField(auto_now_add=True)
#property
def first_word(self):
return self.message.split(" ")[0]
I'm interested to evaluate first word of each message and do some aggregation. First word of each message could vary in length thus I can't use Left, Right or Substr database functions:
queryset = Chat.objects.annotate(command=F("first_word")).values("command")
.annotate(counts=Count("command"))
.order_by("-counts")
Obviously my queryset is not working because I can't call model methods in database query. However is there a way I can do this by using some other database functions?
Related
I have a class/model in my models.py that can receive a textField. I want to add a column in my database that corresponds to the length of this textField. What is the best way of doing that?
You can add a field that stores the character count value to your model:
class YourModel(models.Model):
my_text_field = models.TextField()
char_count = models.DecimalField(max_digits=3, decimal_places=0, blank=True, null=True)
And override the model save method to update the field on save:
def save(self, *args, **kwargs):
if self.my_text_field:
self.char_count = len(self.my_text_field)
super().save(*args, **kwargs)
Adjust the field options (like max_digits) to suit your needs.
It depends how you want to use the length. The answer with the model property (#property) is good if you don't want to do complex queries involving the length, such as filtering by length, ordering, etc.
The solution with an extra database field solves the problem above, but it puts on you the duty of keeping the char_count column updated. If you need the length in your queries, a better way would be to use the database LENGTH function, which can be done readily in Django without changing your model. Examples, with a Article model that has a text CharField:
Order by article text length:
from django.db.models.functions import Length
sorted_qs = Article.objects.order_by(Length('text'))
If you print str(sorted_qs.query), you can see ORDER BY LENGTH("app_article"."text") ASC in the query.
Filter by length greater than x:
Article.objects.annotate(text_len=Length('text')).filter(text_len__gte=x)
See the doc on aggregation.
If you have a large database table, consider adding an index on the length. In PostgreSQL, that would be:
CREATE INDEX app_article_text_length_idx ON app_article (LENGTH(text));
You can define a function in your model that returns length of the TextField field.
Suppose your model is like this:
class ModelName(models.Model):
str_field = models.TextField()
#property
def length(self):
return (self.str_field)
And my_model is one object of this model class, below code returns length of str_field
my_model.length
This way doesn't add a column to database table but you can access the length of str_field.
i'm trying to call back unique constraints field , in my project i have to count number of M2M selected
class Booking(models.Model):
room_no = models.ForeignKey(Room,on_delete=models.CASCADE,blank=True,related_name='rooms')
takes_by = models.ManyToManyField(Vistor)
#property
def no_persons(self):
qnt = Booking.objects.filter(takes_by__full_information=self).count()#but this doesnt work
return qnt
Cannot query "some room information": Must be "Vistor" instance.
class Vistor(models.Model):
full_name = models.CharField(max_length=150)
dob = models.DateField(max_length=14)
city = models.ForeignKey(City,on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(fields=['full_name','dob','city'],name='full_information')
]
def __str__(self):
return f'{self.full_name} - {self.city} - {self.dob}'
it it possible to access full_information through Booking model ? thank you ..
If you want to count the number of Visitors related to that booking, you can count these with:
#property
def no_persons(self):
self.taken_by.count()
This will make an extra query to the database, therefore it is often better to let the database count these in the query. You can thus remove the property, and query with:
from django.db.models import Count
Booking.objects.annotate(
no_persons=Count('takes_by')
)
The Bookings that arise from this QuerySet will have an extra attribute no_persons with the number of related Visitors.
Maybe I misunderstand the purpose of Django's update_or_create Model method.
Here is my Model:
from django.db import models
import datetime
from vc.models import Cluster
class Vmt(models.Model):
added = models.DateField(default=datetime.date.today, blank=True, null=True)
creation_time = models.TextField(blank=True, null=True)
current_pm_active = models.TextField(blank=True, null=True)
current_pm_total = models.TextField(blank=True, null=True)
... more simple fields ...
cluster = models.ForeignKey(Cluster, null=True)
class Meta:
unique_together = (("cluster", "added"),)
Here is my test:
from django.test import TestCase
from .models import *
from vc.models import Cluster
from django.db import transaction
# Create your tests here.
class VmtModelTests(TestCase):
def test_insert_into_VmtModel(self):
count = Vmt.objects.count()
self.assertEqual(count, 0)
# create a Cluster
c = Cluster.objects.create(name='test-cluster')
Vmt.objects.create(
cluster=c,
creation_time='test creaetion time',
current_pm_active=5,
current_pm_total=5,
... more simple fields ...
)
count = Vmt.objects.count()
self.assertEqual(count, 1)
self.assertEqual('5', c.vmt_set.all()[0].current_pm_active)
# let's test that we cannot add that same record again
try:
with transaction.atomic():
Vmt.objects.create(
cluster=c,
creation_time='test creaetion time',
current_pm_active=5,
current_pm_total=5,
... more simple fields ...
)
self.fail(msg="Should violated integrity constraint!")
except Exception as ex:
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(ex).__name__, ex.args)
self.assertEqual("An exception of type IntegrityError occurred.", message[:45])
Vmt.objects.update_or_create(
cluster=c,
creation_time='test creaetion time',
# notice we are updating current_pm_active to 6
current_pm_active=6,
current_pm_total=5,
... more simple fields ...
)
count = Vmt.objects.count()
self.assertEqual(count, 1)
On the last update_or_create call I get this error:
IntegrityError: duplicate key value violates unique constraint "vmt_vmt_cluster_id_added_c2052322_uniq"
DETAIL: Key (cluster_id, added)=(1, 2018-06-18) already exists.
Why didn't wasn't the model updated? Why did Django try to create a new record that violated the unique constraint?
The update_or_create(defaults=None, **kwargs) has basically two parts:
the **kwargs which specify the "filter" criteria to determine if such object is already present; and
the defaults which is a dictionary that contains the fields mapped to values that should be used when we create a new row (in case the filtering fails to find a row), or which values should be updated (in case we find such row).
The problem here is that you make your filters too restrictive: you add several filters, and as a result the database does not find such row. So what happens? The database then aims to create the row with these filter values (and since defaults is missing, no extra values are added). But then it turns out that we create a row, and that the combination of the cluster and added already exists. Hence the database refuses to add this row.
So this line:
Model.objects.update_or_create(field1=val1,
field2=val2,
defaults={
'field3': val3,
'field4': val4
})
Is to semantically approximately equal to:
try:
item = Model.objects.get(field1=val1, field2=val2)
except Model.DoesNotExist:
Model.objects.create(field1=val1, field2=val2, field3=val3, field4=val4)
else:
item = Model.objects.filter(
field1=val1,
field2=val2,
).update(
field3 = val3
field4 = val4
)
(but the original call is typically done in a single query).
You probably thus should write:
Vmt.objects.update_or_create(
cluster=c,
creation_time='test creaetion time',
defaults = {
'current_pm_active': 6,
'current_pm_total': 5,
}
)
(or something similar)
You should separate your field:
Fields that should be searched for
Fields that should be updated
for example:
If I have the model:
class User(models.Model):
username = models.CharField(max_length=200)
nickname = models.CharField(max_length=200)
And I want to search for username = 'Nikolas' and update this instance nickname to 'Nik'(if no User with username 'Nikolas' I need to create it) I should write this code:
User.objects.update_or_create(
username='Nikolas',
defaults={'nickname': 'Nik'},
)
see in https://docs.djangoproject.com/en/3.1/ref/models/querysets/
This is already answered well in the above.
To be more clear the update_or_create() method should have **kwargs as those parameters on which you want to check if that data already exists in DB by filtering.
select some_column from table_name where column1='' and column2='';
Filtering by **kwargs will give you objects. Now if you wish to update any data/column of those filtered objects, you should pass them in defaults param in update_or_create() method.
so lets say you found an object based on a filter now the default param values are expected to be picked and updated.
and if there's no matching object found based on the filter then it goes ahead and creates an entry with filters and the default param passed.
I am trying to fetch data where the column value "tag" belongs to list from the table "UserBookmark".
UserBookmark.objects.filter(tag__in = ['Java','Android'])
but this returns QuerySet[](null set) whereas I do have data matching this query in table
<QuerySet [<UserBookmark: 21 user12 http://careers.bankofamerica.com/ [u'Java']>,<UserBookmark: 22 user12 http://aehlke.github.io/tag-it/examples.html [u'Data Science,Python']>,<UserBookmark: 23 user13 https://github.com/Azure/azure-quickstart-templates [u'Android']>, <UserBookmark: 24 user14 https://github.com/sunnykrGupta/Bigquery-series [u'Python']>, <UserBookmark: 25 user14 https://github.com/ctfs/write-ups-2017 [u'Data Analytics']>]>
models.py
class UserBookmark(models.Model):
user = models.ForeignKey(User)
bookmark = models.URLField()
tag = models.CharField(max_length = 100)
def __str__(self):
return '%i %s %s %s'%(self.id,self.user,self.bookmark,self.tag)
i have modified my models.py
class UserBookmark(models.Model):
user = models.ForeignKey(User)
bookmark = models.URLField()
tags = models.ManyToManyField('Tag',blank=True)
def __str__(self):
return '%i %s %s'%(self.id,self.user,self.bookmark)
class Tag(models.Model):
name = models.CharField(max_length=100, unique=True)
But when i run python manae.py migrate after python managepy makemigrations, I get this error:
ValueError: Cannot alter field bookmark.UserBookmark.tags into bookmark.UserBookmark.tags - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)
What am i doing wrong here?
The problem is not in your query, but in the way you are storing your data. You have a single CharField, and you seem to be populating it by simply converting a list to a string. So your records contain for example the literal string "[u'Data Science,Python']".
If you want to store this kind of tag, you need to store the tags separately. One way to do this would be to set up a separate Tag model and use a many-to-many relationship. There are various third-party packages that do this for you - one example is django-taggit.
Try to pluralize, maybe using tags__in
I have a simple Relation model, where a user can follow a tag just like stackoverflow.
class Relation(models.Model):
user = AutoOneToOneField(User)
follows_tag = models.ManyToManyField(Tag, blank=True, null=True, through='TagRelation')
class TagRelation(models.Model):
user = models.ForeignKey(Relation, on_delete=models.CASCADE)
following_tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
pub_date = models.DateTimeField(default=timezone.now)
class Meta:
unique_together = ['user', 'following_tag']
Now, to get the results of all the tags a user is following:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
tags_following = kakar.relation.follows_tag.all()
This is fine.
But, to access intermediate fields I have to go through a big list of other queries. Suppose I want to display when the user started following a tag, I will have to do something like this:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
kakar_relation = Relation.objects.get(user=kakar)
t1 = kakar.relation.follows_tag.all()[0]
kakar_t1_relation = TagRelation.objects.get(user=kakar_relation, following_tag=t1)
kakar_t1_relation.pub_date
As you can see, just to get the date I have to go through so much query. Is this the only way to get intermediate values, or this can be optimized? Also, I am not sure if this model design is the way to go, so if you have any recomendation or advice I would be very grateful. Thank you.
You need to use Double underscore i.e. ( __ ) for ForeignKey lookup,
Like this :
user_tags = TagRelation.objects.filter(user__user__email="kakar#gmail.com").values("following_tag__name", "pub_date")
If you need the name of the tag, you can use following_tag__name in the query and if you need id you can use following_tag__id.
And for that you need to iterate through the result of above query set, like this:
for items in user_tags:
print items['following_tag__name']
print items['pub_date']
One more thing,The key word values will return a list of dictionaries and you can iterate it through above method and if you are using values_list in the place of values, it will return a list of tuples. Read further from here .