I'm a beginner in MongoDB and I'm trying to solve one problem I had with a relational database (using Django). I have a Song and Album models, and I want each song to have a reference to its Album, and each Album to have a list of all its songs. Is it possible to have something like this in MongoDB :
{
'song_title': 'In mist she was standing',
'album': {
'album_title': 'Orchid',
'songs': [ { song_title: 'Requiem', album:{'album_title':'Orchid', 'songs':[{song_title'....
And that's when we have an endless loop. Is there anyway to achieve this? Maybe I'm not fully understanding the point of MongoDB, I'd be happy if someone could point me to the right direction.
Thanks!
My suggestion is to keep it simple at start: two collections Album and Song just like in any other relational database.
I'm assuing you're using mongoengine as ORM.
class Album(Document):
album_title = StringField()
songs = ListField(ObjectIdFIeld())
meta = {
'indexes': ['songs'],
'allow_inheritance': False,
}
class Song(Document):
song_title = StringField()
album = ObjectIdField()
meta = {
'indexes': ['album']
}
every time you fetch an Album you can fetch its songs with a second query (mongodb has no concept of joins):
album = Album.objects.all().first() # just an example
songs = Song.objects.filter(id__in=album.songs)
Another possibility is to completely embed song into album:
class Song(EmbeddedDocument):
song_title = StringField()
class Album(Document):
album_title = StringField()
songs = ListField(EmbeddedDocumentField(Song))
Could be better if your queries are (almost) all coming from the Album model.
Anyways I suggest starting with the two table setup and then later on you can improve preformance by embedding song data into album.
Hope this helps
Related
I have 2 models which i wanna join
class CollectionBook(models.Model):
collection = models.ForeignKey('Collection')
book = models.ForeignKey('Book')
class Meta:
unique_together = (('collection', 'book'))
class Book(models.Model):
main_author = models.ForeignKey('Author', verbose_name=u'Main author')
publisher = models.ForeignKey('Publisher', verbose_name=u'Publisher')
title = models.CharField(unique=False, max_length=255, verbose_name=u'Title')
text = models.TextField(verbose_name=u'Book text', max_length=523000)
I tried to do it this way
book = Book.objects.extra(
select = {'id':'mainapp_collectionbook.book_id'})
book.query.join((None,'mainapp_collectionbook',None,None))
connection = (
Book._meta.db_table,
CollectionBook.book.field,
)
book.query.join(connection, promote=True)
But it didn't work out and gave me error
Could you offer me another pythonic solutions of this problem or improve my way of doing it, I don't wanna write sql query, I hope that there are better django orm functions for this
Taking the clarification from the comment:
I have another table "Collection". I want select books where collection to which book belongs is in allowed collection list. I generate collection list before this query
First, replace your explicit CollectionBook table with a ManyToManyField on either Book or Collection. For this example I'll assume it's on Book, since that keeps the syntax clearer and the Collection model isn't shown.
class Book(models.Model):
main_author = models.ForeignKey('Author', verbose_name=u'Main author')
publisher = models.ForeignKey('Publisher', verbose_name=u'Publisher')
title = models.CharField(unique=False, max_length=255, verbose_name=u'Title')
text = models.TextField(verbose_name=u'Book text', max_length=523000)
collection = models.ManyToManyField('Collection')
Then you can use __ syntax to follow relationships:
Books.objects.filter(collection__in=SOME_LIST_OF_COLLECTIONS).distinct()
If you need additional information on the book/collection relation, EG a date collected or something, specify a through argument to the ManyToManyField.
if I understand correctly, you can try it, but don't forget to change the YOU_CONDITION
allow_collections = CollectionBook.objects.filter(YOU_CONDITION)
books_pks = allow_collections.values_list('book__pk', flat=true)
Book.objects.filter(pk__in=books_pks)
Hello folks Im new to Django(I have just the finished the tutorial) but I think i understand the basic concepts of it .Im writing here because Im trying to do something "difficult" for my current experience with django and searching the internet didnt give me a solution .What im trying to do is to create a dynamic model based on the number of entries of another model .To be more exact lets say i got the following model :
class criteria(models.Model):
criteria_text = models.CharField(max_length=200)
monotonicity = models.CharField(max_length=1,choices=(('+','ASCEDING'),('-','DESCENDING')),default='+',verbose_name='Monotonicity')
worst = models.IntegerField(default=0)
best = models.IntegerField(default=0)
What i want to do is create all the criteria models instances i want through the django admin panel and then query for all the creteria_text instances in the database and make a model with an attribute for every criteria_text instance.
So lets say I add the following criteria to the database(these are criteria_text attributes of criteria objects: Color,Weight,Price .
I want to end up with a model like this :
class Alternative(models.Model):
Color = models.IntegerField(default=0)
Weight = models.IntegerField(default=0)
Price = models.IntegerField(default=0)
The thing is that in my application this one has to happen a lot of times so i cannot make model each time someone adds an Alternative based on different criteria .
After searching i found that i can define dynamic models using the following format :
attrs = {
'name': models.CharField(max_length=32),
'__module__': 'myapp.models'
}
Animal = type("Animal", (models.Model,), attrs)
So the question is how can I define "attrs" based on a query that gets all the criteria in the database ?Can i define a relationship of this dynamic model with another model ? Also the models already created should be updated if a user adds more criteria .
Is something like this possible ?
If so please show me the path .
I don't think defining dynamic models is a good solution here (or anywhere, really). Rather, you need a relationship that can have as many items as there are criteria instances.
It might be something like this:
class Alternative(models.Model):
name = models.CharField(...)
choices = models.ManyToManyField("Criteria", through="AlternativeChoice")
class AlternativeChoice(models.Model):
alternative = models.ForeignKey('Alternative')
criteria = models.ForeignKey('Criteria')
value = models.IntegerField(default=0)
The real logic will belong in the form, where you will need to create options for each criteria entry, and validate the AlternativeChoice dependent on the related criteria.
Django foreign keys are driving me crazy! I'm new to Django, and I've been working on a solution to what I know must be a very simple problem for over three weeks with no success. I've searched for the answers to my questions, but little has helped.
I have a model similar to the following to support each person's ability to have multiple phone numbers and addresses:
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
<...>
class Phone(models.Model):
person = models.ForeignKey(Person)
<...>
number = PhoneNumberField()
class Address(models.Model):
person = models.ForeignKey(Person)
<...>
zipcode = models.CharField(max_length=10)
I have two questions:
1) When joining Person, Phone, and Address is this the most efficient way?
person = Person.objects.get(pk=1)
phone = Phone.objects.get(person=person)
address = Address.objects.get(person=person)
2) When serializing this model to JSON I'm using Wad of Stuff Django Serializers version 1.1.0. The following code returns only Person data, yet I need Person and the related Phone and Address. What is wrong?
print serializers.serialize('json', Person.objects.all(), indent=4, relations=('phone', 'address',))
Thank you so much for any help you can give!
Edit: To clarify, I believe my inability to replicate the following using Django's ORM is at the root of my problems (or misunderstandings):
select * from person
left join phone
on phone.person_id = person.id
left join address
on address.person_id = person.id
where person.id = 1
1) No.
person = Person.objects.get(pk=1)
phones = person.phone_set.all()
addresses = person.address_set.all()
Also read the docs for select_related
1) You should be able to use this to get the person and his phones/addresses, since it is not a ManyToMany relationship:
person = Person.objects.get(id=1)
phones = Phone.objects.filter(person__id=person.id)
addresses = Address.objects.filter(person__id=person.id)
Most important here is that you don't want to use get() it will throw an error if more than one record is returned. Get() is for getting one single record, filter() is for multiple.
2) I'm not sure here, but you may need to use separate JSON dictionaries for your person, phones, and addresses. I am not familiar with Wad of Stuff, you may want to look at the source code to see what serializers.serialize() is expecting, and which arguments are defining what you get. Sorry, I can't be of help there.
I am trying to add an additional custom field to a django model. I have been having quite a hard time figuring out how to do the following, and I will be awarding a 150pt bounty for the first fully correct answer when it becomes available (after it is available -- see as a reference Improving Python/django view code).
I have the following model, with a custom def that returns a video count for each user --
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
positions = models.ManyToManyField('Position', through ='PositionTimestamp', blank=True)
def count(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute(
"""SELECT (
SELECT COUNT(*)
FROM videos_video v
WHERE v.uploaded_by_id = p.id
OR EXISTS (
SELECT NULL
FROM videos_videocredit c
WHERE c.video_id = v.id
AND c.profile_id = p.id
)
) AS Total_credits
FROM userprofile_userprofile p
WHERE p.id = %d"""%(int(self.pk))
)
return int(cursor.fetchone()[0])
I want to be able to order by the count, i.e., UserProfile.objects.order_by('count'). Of course, I can't do that, which is why I'm asking this question.
Previously, I tried adding a custom model Manager, but the problem with that was I also need to be able to filter by various criteria of the UserProfile model: Specifically, I need to be able to do: UserProfile.objects.filter(positions=x).order_by('count'). In addition, I need to stay in the ORM (cannot have a raw sql output) and I do not want to put the filtering logic into the SQL, because there are various filters, and would require several statements.
How exactly would I do this? Thank you.
My reaction is that you're trying to take a bigger bite than you can chew. Break it into bite size pieces by giving yourself more primitives to work with.
You want to create these two pieces separately so you can call on them:
Does this user get credit for this video? return boolean
For how many videos does this user get credit? return int
Then use a combination of #property, model managers, querysets, and methods that make it easiest to express what you need.
For example you might attach the "credit" to the video model taking a user parameter, or the user model taking a video parameter, or a "credit" manager on users which adds a count of videos for which they have credit.
It's not trivial, but shouldn't be too tricky if you work for it.
"couldn't you use something like the "extra" queryset modifier?"
see the docs
I didn't put this in an answer at first because I wasn't sure it would actually work or if it was what you needed - it was more like a nudge in the (hopefully) right direction.
in the docs on that page there is an example
query
Blog.objects.extra(
select={
'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
},
)
resulting sql
SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count
FROM blog_blog;
Perhaps doing something like that and accessing the user id which you currently have as p.id as appname_userprofile.id
note:
Im just winging it so try to play around a bit.
perhaps use the shell to output the query as sql and see what you are getting.
models:
class Positions(models.Model):
x = models.IntegerField()
class Meta:
db_table = 'xtest_positions'
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
positions = models.ManyToManyField(Positions)
class Meta:
db_table = 'xtest_users'
class Video(models.Model):
usr = models.ForeignKey(UserProfile)
views = models.IntegerField()
class Meta:
db_table = 'xtest_video'
result:
test = UserProfile.objects.annotate(video_views=Sum('video__views')).order_by('video_views')
for t in test:
print t.video_views
doc: https://docs.djangoproject.com/en/dev/topics/db/aggregation/
This is either what you want, or I've completely misunderstood!.. Anywhoo... Hope it helps!
How can I make an select with two (or more) joined tables using Django Models?
For example:
class Album(models.Model):
artist = models.ForeignKey(Musician)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
class Song(models.Model):
album = models.ForeignKey(Album)
name = models.CharField(max_length=100)
num_stars = models.IntegerField()
SELECT * from album, song where (album.id = song.album_id) and (album.artist_id = X)
Django querysets want to return lists of model instances, which doesn't quite map to the the SQL notion of returning data from multiple tables joined into a single result table.
To achieve the effect of the SQL query you provide as an example, you could do one of the following:
1) If you want to iterate over your data by Songs, you can limit the songs you query by the artist id, like so:
songs = Song.objects.filter(album__artist__id=123)
for song in songs:
print song.name
print song.album.name
... do what ever you need to do with song here ...
If you are concerned about performance, you can add select_related() to your queryset.
# the depth argument tells django to only follow foreign key relationships
# from Song one level deep
songs = Song.objects.select_related(depth=1).filter(album__artist__id=123)
for song in songs:
# django has now already fetched the album data, so accessing it
# from the song instance does not cause an additional database hit
print song.album.name
2) If you want to iterate over your data by Album, you can do things like so:
albums = Album.objects.filter(artist__id = 123)
for album in albums:
for song in album.song_set.all():
print song.name
print album.name
... etc ...
You can perform raw sql query, but that's probably not the thing you want to do. Explain how your Models look like and what you want to achieve and probably django ORM will be enough to perform the query.