I have two models that look like this:
class Author(models.Model):
name = models.CharField(max_length=200)
class Book(models.Model):
author = models.ForeignKey('Author', on_delete=models.CASCADE)
isbn_id = models.CharField(max_length=50, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
How can I make a query such that:
I get each author with the total number of books created, number of books with an isbn.
Each time a book is created for an author, I can get the last_created, last_modified.
This is an example of the result I am trying to achieve;
s/n| author| no_books_with_isbn| all_books| last_created| last_modified
1. Faye 2 2 ... ....
2. Soya 2 5
3. Jake 6 6
4. Gurj 4 4
5. Zoom 2 10
You need to annotate lots of aggregations to your queryset (Reference: Aggregation [Django Docs]). To get the counts you can use the Count [Django Docs] function and for the last_created / last_modified you can use the Max [Django Docs] function to achieve what you want:
from django.db.models import Count, Max, Q
queryset = Author.objects.annotate(
all_books=Count('book'),
no_books_with_isbn=Count(
'book',
filter=Q(book__isbn_id__isnull=False)
),
last_created=Max('book_created_at'),
last_modified=Max('book_updated_at')
)
for author in queryset:
print(author.name, author.no_books_with_isbn, author.all_books, author.last_created, author.last_modified)
Related
I am writing a query in Laravel 8 using Eloquent. There is a many-to-many relation between subjects and streams and their pivot table is courses.
subjects:
id
name
streams
id
name
courses
id
subject_id
stream_id
I have to fetch subjects with specific stream.
I have written the following query.
$this->subjects = Subject::has('streams',$this->stream)->get();
I am having problem in this.
courses table :
id
stream_id
subject_id
1
1
1
2
1
2
3
1
3
4
1
4
5
2
1
6
2
2
7
3
1
This is the sample data. If I want to fetch subject where stream id is 1 then it is only fetching subject with ids 3 and 4 and not fetching subjects with id 1 and 2. Can anyone explain me why it is skipping subject ids with 1 and 2 ?
PLease anyone can help me with this.
Thank you in advance . :)
Edited
class Subject
{
public function streams()
{
return $this->belongsToMany(Stream::class, 'courses',
'subject_id', 'stream_id');
}
}
class Stream
{
public function subjects()
{
return $this->belongsToMany(Subject::class, 'courses',
'stream_id', 'subject_id');
}
}
For fetching models having a specific related model you can use whereHas() method. https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
Assuming that $this->stream is a Stream model, you can use a closure for whereHas method:
$stream = $this->stream;
$this->subjects = Subject::whereHas('streams', function($query) use ($stream){
$query->where('streams.id', $stream->id);
})->get();
Edit: since $this->stream is an id you should do
$stream = $this->stream;
$this->subjects = Subject::whereHas('streams', function($query) use ($stream){
$query->where('streams.id', $stream);
})->get();
You should specify the table name where you are referencing the id to avoid ambiguity.
$this->subjects = Subject::whereHas('streams', function($query) use ($stream){
$query->where('stream.id', $stream->id);
})->get();
I have 2 tables (Product and CustomerProduct)
CustomerProduct is the intermediate table between Customer and Product. It assigns customer specific pricing to certain products.
Product Model
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=12, decimal_places=2)
sample data
id
name
price
1
orange
1.5
2
apple
2.2
3
kiwi
3.5
CustomerProduct Model
class CustomerProduct(models.Model):
customer = models.ForeignKey(
"Customer",
on_delete=models.CASCADE,
related_name="customer_customerproducts",
)
product = models.ForeignKey(
"Product",
on_delete=models.CASCADE,
related_name="product_customerproducts",
)
price = models.DecimalField(
max_digits=12,
decimal_places=2,
)
sample data
id
customer_id
product_id
price
1
233
1
1.2
2
233
2
2.0
Expected Result
I want to query the all products but the Product.price adjusted according to CustomerProduct.price if related field exists. The expected data (example in json but want queryset):
[
{
id: 1
name: "orange"
price: 1.2 // The price adjusted according to customer price
}
{
id: 2
name: "apple"
price: 2.0 // The price adjusted according to customer price
}
{
id: 3
name: "kiwi"
price: 3.5 // Price remain original because CustomerProduct not exists.
}
]
The How
I totally have no idea how to accomplish this in Django. How to do that?
You can use Coalesce [Django docs] with Subquery expressions [Django docs] to annotate a value on your queryset which will be the price that you want. Coalesce gives the first non-null value of the expressions passed to it, so we will pass it a subquery that will get the related price from CustomerProduct or the field price itself in that order to achieve what you want:
from django.db.models import F, OuterRef, Subquery
from django.db.models.functions import Coalesce
customer = Customer.objects.get(pk=some_pk) # get the customer for which you want the results
# Last related price, will use it as a subquery
related_price = CustomerProduct.objects.filter(
product=OuterRef('pk'),
customer=customer,
).order_by('-pk').values('price')[:1]
products = Product.objects.annotate(
price_offered=Coalesce(
Subquery(related_price), # If there is a matching object in the subquery use it
F('price'), # Else use the price of the Product itself
)
)
for product in products:
print(product.pk, product.name, product.price_offered)
I'm developing a reddit-like site where votes are stored per-user (instead of per-post). Here's my relevant schema:
content
id | author_id | title | text
---|-----------|-------------|---
1 | 1 (adam) | First Post | This is a test post by adam
vote: All the votes ever voted by anyone on any post
id | voter_id | content_id | category_id
---|-------------|------------------|------------
1 | 1 (adam) | 1 ("First Post") | 1 (upvote)
2 | 2 (bob) | 1 ("First Post") | 1 (upvote)
vote_count: Current tally ("count") of total votes received by a post by all users
id | content_id | category_id | count
---|------------------|--------------|-------
1 | 1 ("First Post") | 1 (upvote) | 2
I've defined a voteCount relation in Objection.js model for the content table:
class Content extends Model {
static tableName = 'content';
static relationMappings = {
voteCount: {
relation: Model.HasManyRelation,
modelClass: VoteCount,
join: {
from: 'content.id',
to: 'vote_count.content_id'
}
}
}
}
But I recently (learned and) decided that I don't need to keep (and update) a separate vote_count table, when in fact I can just query the vote table and essentially get the same table as a result:
SELECT content_id
, category_id
, COUNT(*) AS count
FROM vote
GROUP
BY content_id
, category_id
So now I wanna get rid of the vote_count table entirely.
But it seems that would break my voteCount relation since there won't be a VoteCount model (not shown here but it's the corresponding the model for the vote_count table) no more either. (Right?)
How do I keep voteCount relation while getting rid of vote_count table (and thus VoteCount model with it)?
Is there a way to somehow specify in the relation that instead of looking at a concrete table, it should look at the result of a query? Or is it possible to define a model class for the same?
My underlying database in PostgreSQL if that helps.
Thanks to #Belayer. Views were exactly the solution to this problem.
Objection.js supports using views (instead of table) in a Model class, so all I had to do was create a view based on the above query.
I'm also using Knex's migration strategy to create/version my database, and although it doesn't (yet) support creating views out of the box, I found you can just use raw queries:
module.exports.up = async function(knex) {
await knex.raw(`
CREATE OR REPLACE VIEW "vote_count" AS (
SELECT content_id
, category_id
, COUNT(*) AS count
FROM vote
GROUP
BY content_id
, category_id
)
`);
};
module.exports.down = async function(knex) {
await knex.raw('DROP VIEW "vote_count";');
};
The above migration step replaces my table vote_count for the equivalent view, and the Objection.js Model class for it (VoteCount) worked as usual without needing any change, and so did the relation voteCount on the Content class.
I am creating a small Django project which show stats collected from twitter data
for example my tables are
hashDetails
---------------------------------------------
id hashname tweetPosted trendDate userid
---------------------------------------------
1 #abc 44 2-2-2016 #xyz
2 #abc 55 2-2-2016 #qwer
3 #xcs 55 3-2-2016 #qwer
4 #xcs 55 4-2-2016 #qwer
---------------------------------------------
userDetails
----------------------------------------------
id userid profileImage profileImage
----------------------------------------------
1 #xyz image2.jpg www.abc.com
2 #qwer image3.jpg www.xadf.com
----------------------------------------------
for this if i create models.py
class userDetails(models.Model):
userid= models.CharField(max_length=30)
profileImage= models.CharField(max_length=30)
profileImage= models.CharField(max_length=30)
class hashDetails(models.Model):
hashname= models.CharField(max_length=30)
tweetPosted= models.IntegerField()
trendDate= models.DateTimeField()
userid = models.ForeignKey(userDetails, to_field ='userid')
but i don't wanna make userid unique cause
i want something like i can enter data in both table manually
and when i query in my view it will search result from both table
example
if i want all trends by #xyz
or if i want list of all users who did #abc trend
or if i want result of all trends in specific date
in short i want both table to behave like one
I can't use userid as unique my daily data will be about 20MB so you can assume its difficult to find ids
I found one solution of my problem and its working for me
i just create normal 2 model without foreignkey or any relation
and define my function in views.py
and got my result what i want
def teamdetail(request,test_id):
hashd = hashDetails.objects.get(hashname=test_id)
userd= userDetails.objects.all()
context = {'hashinfo': hashd, 'username':userd}
return render(request,'test/hashDetails.html',context)
Here is the situation:-
I have a table called Users. This contains user data for students and tutors as most of the data required is the same.
Having completed the system I am now told that the client would like to be able to assign students to tutors.
Is there a legitimate/ clean way I can create a one to many relationship within a single table, perhaps via a link table?
I've tried to think this through but whatever solution I come up with seems messy.
I would be grateful for any input.
Thanks
Phill
Have you tried the following approach?
Make a new table, for example TutorStudent (choose a more appropriate name if needed). It should have two columns:
Tutor_ID
Student_ID
Both columns shall be the (composite) primary key, each column will be a foreign key to your Users table User_ID (I assume this is what you have).
So, if you have a tutor named Newton that has two students, Tesla and Edison, your Users table will have something like this:
User_ID, Name
1, Newton
2, Tesla
3, Edison
and your TutorStudent table will have following values:
Tutor_ID, Student_ID
1, 2
1, 3
Relatively simple and doesn't require any modifications to your existing table.
Do take care when deleting users - use the delete cascade feature of your database system or do some maintenance work afterwards so your TutorStudent table doesn't go stale when updating/removing your users.
My ideal for the same situation
Example: one book have many category:
Basic solution:
book table has recorded book information
category table has recored category information ex: 100 documents
book_category_relation table has single book (book_id) has category(category_id) 1 book may be have 100 category_id
Ideal solution:
First calculate total your category: ex 100 document. Each category equal value 1 bit: max 31 bit so 100 category we have ceil floor(100%31) = 4 groups
category_id = 1 : 1 (1%31) <=> 000000001 group 0 = floor(1/31)
category_id = 2 : 2 (2%31)<=> 000000010 group 0 = floor(2/31)
category_id = 3 : 4 (3%31)<=> 000000100 group 0 = floor(3/31)
category_id = 4 : 8(4%31)<=> 000001000 group 0 = floor(4/31)
...........................
category_id = 31: 2^31(31%31) <=>1000..000 group 0 if moduler 31 equal zero so number group = (31/31 -1)=0;
category_id = 32: 1(32%31) <=> 0000000001 group 1 = floor(32/31)
category_id = 33: 2(33%31) <=> 0000000010 group 1 = floor(33/31)
Ok now we add 4 fields in design book table (group_0,group_1,group_2,group_3) with int(11) unsigned and add index that fields
if book has category id = n so we can the following calculate formula:
bit code = (n%31 ==0)?31: (n%31)
number group field = (n%31==0)?(n/31 -1):floor(n/31)
ex: book in category_id = 100 so:
bit code = (100%31) =7 <=>2^7 = 128,
group = floor(100%31) = 3 <=> in group_3
so if you need query all book in category_id = 100, query string is:
SELECT * FROM book WHERE group_3&128
Note: MySQL not index working if bitwise in where.
But you can check in this link:
Bitwise operations and indexes