Django one-to-many queries - sql

Django newbie here.
I created three models: Band, Album and AlbumType (Live, EP, LP). Album have two foreign keys (from band and albumtype). Ok, so, in the view a make something like this:
bandList = Band.objects.all()
To retrieve all the bands but I can't figure out how to get all the Albums from a Band in the same view.
Any help will be appreciated. Thanks.

By default the related objects (through a ForeignKey on the other model) are accessible trough <modelname>_set zo in this case that is band.album_set (note this is a Manager attribute so you will probably use band.album_set.all() most of the time).
I personally prefer to give al my ForeignKey fields a related_name so I can name the attribute on the other model. The next example gives the Band the attribute band.albums.
class Band(models.Model):
# Band definition
class Album(models.Model):
band = models.ForeignKey(Band, related_name='albums')
# Rest of album definition

Could be great if you share your models definition. But I hope this helps you:
If you want to retrieve the Albums for a specific band:
band = Band.objects.get(...)
band_albums = Album.objects.filter(band=band)
That will return the albums for a band.
If you want retrive albums for all the bands:
bandList = Band.objects.all().select_related('album_set')
This will return the bans as before, but will cache the albums.

Related

The best way to avoid generic ForeignKey in django

I have Picture model which contains different link to images. I also have People and Car models that may have one or more images. That means that certain pictures can belong to an object in Car or People model.
I try to make ForeignKey but one of the field (car_id or people_id) will be empty.
I can't create an abstract model and make ForeignKey from Picture
The last solution that I know is genericForeignKey but it seems to complex for such trivial task.
Is there are a best way to solve the problem?
I solved the problem like this:
I created another model called Album that has only id
class People(models.Model):
name = models.CharField(max_length=100)
album = models.OneToOneField(Album)
class Car(models.Model):
horse_power = models.IntegerField()
ablum = models.OneToOneField(Album)
class Picture(models.Model):
title = models.CharField(max_length=100)
album = models.ForeignKey(Album)

REST api, POST entity with relationships?

Im having an issue where I cant decide how to procced on this matter.. and I need to know if there is any standard way to solve this.. or if you guys have any great input for this matter.
The thing is that I have started to build a very basic API for learning purpose
The API is a simple Music store.. where the store has some Albums which requires and artist.
So the Relationship is Artist <--- 1 ----- * ---> Albums and for an album to exist its required that that the album has an artist. But the artist doesnt require and Album.
Now to the problem...
Due to this relationship if I want to create a new Album.. I would have to post the album data.. AND the entire Arist-data..not just the ID of th artist.. but the whole Artist.. which doesnt seem very effective if you ask me.. since that a whole lot of unnecessary.
So as I see it there is two ways to solve this...
either I just post the entire album-data/object and allows the Artist-data for the object to only contain an ID which then reference to the artist.
So instead of posting:
{
"Name":"AlbumName",
"Description":"Some description for the album",
"ReleaseYear": "1992",
"Artist" {
"Name":"Some artist",
"Id":"12345",
"Biography":"Lorem ipsum dolor sit amet"
}
}
I would do this:
{
"Name":"AlbumName",
"Description":"Some description for the album",
"ReleaseYear": "1992",
"Artist" {
"Id":"12345"
}
}
Option number two is to actually have a route/url thats specific for this...
for instance:
/api/artist/{artistid}/album
And then simply post an album-object to that url..
But as I said.. Im really not sure of whats right and wrong here.. or is there any standard way of handling this?
Thanks in advance!
I would suggest something like this.
POST /musicstore/artist/343/albums
{
"Name":"AlbumName",
"Description":"Some description for the album",
"ReleaseYear": "1992",
}
The act of creating a resource as a child of the collection of albums for the artist 343 implicitly creates the relationship between the artist and the album. There is no need to specify it in the payload.
Albums and Artists are two separate entities, you shouldn't post all the artist data into Albums, just its ID.
Therefore you first create the artist if it doesn't exist, and after that you post the album details, artist_id being one of its properties.

How to find_or_initialize based on two fields when both fields correspond to possible uninitialized objects

Hi guys I have a situation where on a form I'm taking in orders for a car servicing application. I have the following models:
Car
belongs_to :car_company
Car_company
has_many :cars
Services
attributes_accessible :car_company_id, :car_id
#virtual attributes
attributes_accessible :car_company_name, :car_reg
The thing is that on a single form the user can enter in the name of the car company as well as the registration number of a car. If the company name doesnt exist it creates a new company and associates it with the service and the same goes for the car. I got this part working however the thing is that I want that on submitting this form the car created should be automatically associated with the car_company whether the carcompany exists or doesn't exist.
I'm pretty stuck here on how to get this thing done the right way? Its basically just to avoid having to enter the car details and the company details seperately just to use them on a form. Any ideas guys?
I see you are using an unconventional model name. By convention in rails, your model should be CarCompany. However, I think what you have will work.
Putting something like this in the appropriate controller may be something like what you want. If not, please clarify what you want.
car_company = Car_company.find_or_initialize_by_name(params[:car_company_name])
car = Car.find_or_initialize_by_registration(params[:car_registration])
car_company.cars << car
car_company.save
You actually may be able to combine the middle two lines with car_company.cars.find_or..., but I'm not sure if that works or not.
I hope that helps.

Django aggregate query

I have a model Page, which can have Posts on it. What I want to do is get every Page, plus the most recent Post on that page. If the Page has no Posts, I still want the page. (Sound familiar? This is a LEFT JOIN in SQL).
Here is what I currently have:
Page.objects.annotate(most_recent_post=Max('post__post_time'))
This only gets Pages, but it doesn't get Posts. How can I get the Posts as well?
Models:
class Page(models.Model):
name = models.CharField(max_length=50)
created = models.DateTimeField(auto_now_add = True)
enabled = models.BooleanField(default = True)
class Post(models.Model):
user = models.ForeignKey(User)
page = models.ForeignKey(Page)
post_time = models.DateTimeField(auto_now_add = True)
Depending on the relationship between the two, you should be able to follow the relationships quite easily, and increase performance by using select_related
Taking this:
class Page(models.Model):
...
class Post(models.Model):
page = ForeignKey(Page, ...)
You can follow the forward relationship (i.e. get all the posts and their associated pages) efficiently using select_related:
Post.objects.select_related('page').all()
This will result in only one (larger) query where all the page objects are prefetched.
In the reverse situation (like you have) where you want to get all pages and their associated posts, select_related won't work. See this,this and this question for more information about what you can do.
Probably your best bet is to use the techniques described in the django docs here: Following Links Backward.
After you do:
pages = Page.objects.annotate(most_recent_post=Max('post__post_time'))
posts = [page.post_set.filter(post_time=page.most_recent_post) for page in pages]
And then posts[0] should have the most recent post for pages[0] etc. I don't know if this is the most efficient solution, but this was the solution mentioned in another post about the lack of left joins in django.
You can create a database view that will contain all Page columns alongside with with necessary latest Post columns:
CREATE VIEW `testapp_pagewithrecentpost` AS
SELECT testapp_page.*, testapp_post.* -- I suggest as few post columns as possible here
FROM `testapp_page` LEFT JOIN `testapp_page`
ON test_page.id = test_post.page_id
AND test_post.post_time =
( SELECT MAX(test_post.post_time)
FROM test_post WHERE test_page.id = test_post.page_id );
Then you need to create a model with flag managed = False (so that manage.py sync won't break). You can also use inheritance from abstract Model to avoid column duplication:
class PageWithRecentPost(models.Model): # Or extend abstract BasePost ?
# Page columns goes here
# Post columns goes here
# We use LEFT JOIN, so all columns from the
# 'post' model will need blank=True, null=True
class Meta:
managed = False # Django will not handle creation/reset automatically
By doing that you can do what you initially wanted, so fetch from both tables in just one query:
pages_with_recent_post = PageWithRecentPost.objects.filter(...)
for page in pages_with_recent_post:
print page.name # Page column
print page.post_time # Post column
However this approach is not drawback free:
It's very DB engine-specific
You'll need to add VIEW creation SQL to your project
If your models are complex it's very likely that you'll need to resolve table column name clashes.
Model based on a database view will very likely be read-only (INSERT/UPDATE will fail).
It adds complexity to your project. Allowing for multiple queries is a definitely simpler solution.
Changes in Page/Post will require re-creating the view.

Find all records of a certain type in Polymorphic table using ActiveRecord in Rails 3

I have a table Category that is a polymorphic model for a bunch of other models. For instance
model Address has shipping, billing,
home, work category
model Phone has home, mobile, work,
fax category
model Product has medical, it
equipment, automotive, aerospace, etc
categories.
What I want to be able to do is something like
Product.all_categories and get and array of all categories that are specific to this model.
Of course I can do something like this for each model in question:
Category.select("name").where("categorizable_type = ?","address")
Also pace_car - which is rails 3 ready, allows me to do something like this:
Category.for_category_type(Address)
But I was wondering if there is a more straightforward / elegant solution to this problem using Active Record iteself - without relying on a gem?
Thank you
I'm not aware of anything built-in to ActiveRecord to give you this, but you could set up that for_category_type method in one line of code in your Category controller:
scope :for_category_type, lambda { |class_name| where("categorizable_type = ?", class_name) }