django sql join with like - django

I have following models
class Artist(models.Model):
name = models.CharField(max_length=200, unique=True)
photo = models.CharField(max_length=250)
views = models.IntegerField()
class Meta:
verbose_name = "artist"
ordering = ['-views']
def __unicode__(self):
return self.name
class Song(models.Model):
artist = models.ForeignKey("Artist")
name = models.CharField(max_length=250)
lyrics = models.TextField(max_length=255)
path = models.CharField(max_length=200)
views = models.IntegerField()
date = models.DateField()
class Meta:
verbose_name = "song"
ordering = ['-views']
def __unicode__(self):
return self.name
and would like to get all the rows that match the keyword.
I can do it in sql and returns what I want. here is the sql;
SELECT song.name, song.path FROM song JOIN artist on song.artist_id = artist.id WHERE artist.name LIKE %keyword% || song.name LIKE %keyword% || song.lyrics LIKE %keyword%
but could not figure out how to do that in django. should I use custom sql? do you guys have better idea?
thanks in advance

matching_songs = Song.objects.filter(Q(name__contains=keyword) | Q(lyrics__contains=keyword) | Q(artist__name__contains=keyword))
See Django docs on Q objects for more details

After 2 hours of digging and asking myself why I am not getting expected results, I realised that __contains is case-sensitive while __icontains is not.
So, for example if we have person named "torres" in database and supply "Torres" to __contains then it will return nothing while __icontains will return "Torres".

Related

Syntax to reverse-query a cached queryset

I have the following 3 models related by Foreign Key as following:
class Seller(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Genre(models.Model):
seller= models.ForeignKey(Seller, related_name="genre", on_delete=models.CASCADE)
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Book(models.Model):
genre= models.ForeignKey(Genre, related_name="book", on_delete=models.CASCADE)
name = models.CharField(max_length=20)
def __str__(self):
return self.name
And I want to retrieve the whole 3 tables in one database query, by querying the Seller objects, as following:
sellers = Seller.objects.select_related('genre', 'book').all().values('name')
seller_df = pd.DataFrame(list(sellers))
What is the syntax to filter for books carried by a particular seller, without hitting the database again (by utilizing either the Seller queryset or the pandas seller_df)
seller1 = seller_df ['name'].iloc[0]
seller1_books = Book.objects.filter(...)
seller_last = seller_df ['name'].iloc[-1]
seller_last_books = Book.objects.filter(...)
I dont know so mach about caching but I know something that you like:
We use select_related when the object is single like onetoone or fk.
.
for many to many or reverse fk like your example use prefetch_related

Django - counting foreign keys in models

I know how I can count things with annotate (in my view) but I would like to do the same in model (so it would be more reusable).
For example (lets take an example from django documentation) I have this model:
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
class Publisher(models.Model):
name = models.CharField(max_length=300)
class Book(models.Model):
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
rating = models.FloatField()
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
pubdate = models.DateField()
class Store(models.Model):
name = models.CharField(max_length=300)
books = models.ManyToManyField(Book)
and I can use in view this:
from django.db.models import Count
pubs = Publisher.objects.annotate(num_books=Count('book'))
But how I do that in model?
I know this question is pretty basic (probably) but I'm pretty much beginner in django.
Thanks for answers!
You can use custom managers:
Django docs: Managers
class BookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(num_books=Count('book'))
class Publisher(models.Model):
name = models.CharField(max_length=300)
books = BookManager()
Now you can call it like this:
pubs = Publisher.books.all()
And you will have num_books with your objects.
You can use classmethod for this.
class Publisher(models.Model):
...
#classmethod
def get_book_count(cls):
return cls.objects.annotate(num_books=Count('book'))
You can call this method as
pubs = Publisher.get_book_count()
Edit - Also check out the answer by #Navid2zp which might be a better solution for you.

Django Order_by Not working on FloatField

Order_by not working in FloatField type Django
models.py
class CourseCategory(models.Model):
category = models.CharField(max_length=100, unique=True, null=False)
description = models.TextField(blank=True)
class Meta(object):
app_label = "course_category"
def __unicode__(self):
return self.category
Coursetrack Model
class CourseTrack(models.Model):
category = models.ForeignKey(CourseCategory)
course_id = CourseKeyField(max_length=255, db_index=True)
tracks = models.FloatField(null=True, blank=True, default=None)
active = models.BooleanField(default=True)
class Meta(object):
app_label = "course_category"
def __unicode__(self):
return str(self.course_id)
TopCoursesCategory
class TopCoursesCategory(models.Model):
category = models.ForeignKey(CourseCategory)
class Meta(object):
app_label = "course_category"
def __unicode__(self):
return str(self.category)
I added here order_by(), as you can see but its not working.
view.py
def get_popular_courses_ids():
popular_category_id = CourseCategory.objects.filter(category='Popular')
popular_courses_ids = CourseTrack.objects.values('course_id').filter(category=popular_category_id).order_by('tracks')
course_id_list = []
for course_id in popular_courses_ids:
course_id_list.append(course_id['course_id'])
return course_id_list
I think the query you have posted is wrong.
You have used the following lines.
popular_category_id = CourseCategory.objects.filter(category='Popular')
popular_courses_ids = CourseTrack.objects.values('course_id').filter(category=popular_category_id).order_by('tracks')
In the first line, you have used filter and you have used the resulting variable as category= in your second query which you cannot do. For category= in your second query to work, you would need to give a single element and not a queryset. Replace your filter with get in the first query and it might work fine.
Or
If you think that popular_category_id can have more than one row for the category popular, leave the first query as it is and change your second query to
popular_courses_ids = CourseTrack.objects.values('course_id').filter(category__in=popular_category_id).order_by('tracks')
I have changed category to category__in.

Advice Needed for the Structure of Many-To-Many Fields in Django

I am building a REST-API that will be consumed by an Angular application - this is for my guitar company’s website. There is an Artist Profile page that display an artist’s name, a short bio and a list of the projects(bands) they’re associated with and the date-ranges they were active with them. Here is where things get complicated.
Any given project can be associated with more than one artist - i.e. I could have two guitar players from the same band. I was able to solve that association by creating a many-to-many field and it worked great…until I realized that I have artists who have been in the same band at different times.
I have tried many approaches so far. I wish I could list them, but I kinda lost track. But, the code below is the where I am at right now. I can indeed associate a band with multiple artists, but I can’t associate different date ranges to different artists in the same bands. Any guidance is much appreciated.
class projectDate(models.Model):
begin = models.DateField()
end = models.DateField()
def __str__(self):
string_date_range = self.begin.strftime("%d/%m/%y") + "-" + self.end.strftime("%d/%m/%y")
return string_date_range
class artistProfiles(models.Model):
artist_name = models.CharField(max_length=20)
artist_image = models.URLField()
description = models.TextField(max_length=500)
band_website = models.URLField()
def __str__(self):
return self.artist_name
class artistProjects(models.Model):
project_name = models.CharField(max_length=20)
dates = models.ManyToManyField(projectDate, related_name='date_span')
artists = models.ManyToManyField(artistProfiles, related_name='projects')
def __str__(self):
return self.project_name
class artistSocialMedia(models.Model):
facebook = models.URLField()
twitter = models.URLField()
instagram = models.URLField()
artist = models.ForeignKey(artistProfiles, related_name='social_media', on_delete=models.CASCADE)
def __str__(self):
return self.artist.artist_name
artistProjects and projectDate should not be a many-to-many relationship, since a projectDate is specific to a project and unlikely to be shared by many. You can instead make artistProjects a foreign key in projectDate so that a artistProjects can have many projectDates but not vice versa:
class projectDate(models.Model):
begin = models.DateField()
end = models.DateField()
project = models.ForeignKey(artistProjects, related_name='dates')
Note that your artistProjects represents just one project, so you should avoid giving it a plural name. Naming it artistProject will make your code more readable.
Not sure whether i can solve your problems or not. I am going to describe it in simple way so you can just adjust it with your models.
These is my advice. Hope it solve your problems.
Artist Profile
id (PK)
artist_name
artist_image
description
band_website
Artist Social Media
id (PK)
artist_profile_id (FK)(Artist Profile)
facebook
twitter
instagram
Artist Project
id (PK)
artist_band_project_id (FK)(Artis Band Project)
Artist Band Project
id (PK)
begin
end
Artist Band Project Member
id (PK)
artis_band_project_id (FK)(Artist Band Project)
artis_profile_id (FK)(Artist Profile)
Regards,
Meikelwis Wijaya
#blhsing Ended up being the closest of all answers, but it took a little more massaging to get the relationships and JSON structure I was looking for.
Here is what worked for the models:
from django.db import models
class artistProfile(models.Model):
artist_name = models.CharField(max_length=20)
artist_image = models.URLField()
description = models.TextField(max_length=500)
band_website = models.URLField()
def __str__(self):
return self.artist_name
class artistProject(models.Model):
project_name = models.CharField(max_length=20)
def __str__(self):
return self.project_name
class projectTenure(models.Model):
begin = models.DateField()
# blank and null are allowed here in case an artists is still with a given project
end = models.DateField(blank=True, null=True)
project = models.ForeignKey(artistProject, on_delete=models.CASCADE)
artist = models.ForeignKey(artistProfile, on_delete=models.CASCADE,
related_name='projects')
def __str__(self):
# TODO: find a way to return the related project and artist names
string_date_range = self.begin.strftime("%d/%m/%y") + "-"
return string_date_range
class artistSocialMedia(models.Model):
facebook = models.URLField()
twitter = models.URLField()
instagram = models.URLField()
artist = models.ForeignKey(artistProfile, related_name='social_media',
on_delete=models.CASCADE)
def __str__(self):
return self.artist.artist_name
And here is how I serialized it:
from rest_framework import serializers
from .models import (artistProfile, artistProject, projectTenure, artistSocialMedia)
class artistSocialMediaSerializer(serializers.ModelSerializer):
class Meta:
model = artistSocialMedia
fields = ('facebook', 'twitter', 'instagram')
class artistProjectSerializer(serializers.ModelSerializer):
class Meta:
model = artistProject
fields = ('project_name',)
class projectTenureSerializer(serializers.ModelSerializer):
project_name = serializers.CharField(source='project.project_name')
class Meta:
model = projectTenure
fields = ('project_name', 'begin', 'end')
class artistProfileSerializer(serializers.ModelSerializer):
projects = projectTenureSerializer(many=True, read_only=True)
social_media = artistSocialMediaSerializer(many=True, read_only=True)
class Meta:
model = artistProfile
fields = ('artist_name', 'artist_image', 'description',
'band_website', 'projects', 'social_media')

In Django REST framework, how do you access a field in a table referenced by a foreign key

I have the following models in Django:
class campaign(models.Model):
start_date = models.DateField('Start Date')
end_date = models.DateField('End Date')
description = models.CharField(max_length=500)
name = models.CharField(max_length=200)
active_start_time = models.TimeField()
active_end_time = models.TimeField()
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
client_id = models.ForeignKey('client',on_delete=models.PROTECT)
def __unicode__(self):
return u'%d | %s | %s' % (self.id,self.name, self.description)
class campaign_product(models.Model):
product_id = models.ForeignKey('product',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
campaign_id = models.ForeignKey('campaign',on_delete=models.PROTECT)
class product(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=500)
sku = models.CharField(max_length=200,blank=True,null=True)
retail_price = models.DecimalField(decimal_places=2,max_digits=11)
discount_price = ((1,'Yes'),(0,'No'))
discounted_price = models.DecimalField(decimal_places=2,max_digits=11,blank=True,null=True)
category_id = models.ForeignKey('category',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
def __unicode__(self):
return u'%d | %s' % (self.id, self.name)
I also have the following serializer:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id')
And the following view set behavior in the urls.py file:
class campaignProductViewSet(viewsets.ModelViewSet):
queryset = campaign_product.objects.filter(campaign_id__start_date__lte=datetime.now(),campaign_id__end_date__gte=datetime.now(),campaign_id__active__exact=True)
serializer_class = campaignProductSerializer
My problem is I need to include the name field from the products model in my query results when for instance a request is made on http://127.0.0.1:8000/campaign_product/1/. Currenly this request returns only the product_id and the campaign_id. I tried making the serializer as follows:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product.name')
But then the service returns the following error:
Field name `product.name` is not valid for model `campaign_product`.
I event tried using product__name with and without quotes. Without quotes it tells me that there is no such variable, and with quotes it gives the is not valid for model error similar to the above. Heeelp! Getting this extra field is proving to be a pain :-(
What you want will need to look something more like this:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
product_name = serializers.CharField(source='product_id.name')
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product_name')
P.S. As an unrelated side note, it is generally a convention in Python code to name classes with CamelCase, such as Campaign, CampaignProduct, Product, and CampaignProductSerializer.
Edit: P.P.S. Originally, I had put written the product_name field with source='product.name'. This was actually due to me looking at the code too quickly and making assumptions based on Django conventions. Typically, with a Django ForeignKey, you would name the ForeignKey field after the model you are linking to, rather than explicitly naming it with _id. For example, the CampaignProduct model would typically be written with product = ForeignKey(...) and campaign = ForeignKey(...). In the background, Django will actually use product_id and campaign_id as the database field names. You also have access to those names on your model instances. But the product and campaign variables on your model instances actually return the objects which you are referring to. Hopefully that all makes sense.