I'm using flask as a python framework with sqlalchemy. The models use the query_property which helps me build queries:
class Person(object):
query = db_session.query_property()
....
persons = Person.query.all()
Each person has a city, state and country where he lives in and I want to gather all results and output something like:
Country State City Persons(count)
================================================================================
US NY New York 10
DE Berlin Berlin 100
This obviously needs a select count and group by country, state, city. The group by thing works but I dont know how to implement the counting since query property has no select method in which case I would write:
Person.query.select([Person.country, Person.state, Person.city, func.count(Person.id)]).all()
Anyone with an idea how to use query_property with group_by and count?
You can use the add_columns method on Person.query to append computed values:
Person.query.add_columns(func.count(...)).group_by(...).all()
Keep in mind that you get back a list of tuples (not Person objects). However, each tuple contains the Person object and the computed value:
[(<Person object>, 3), (<Person object>, 7), ...]
You are right - you cannot do it using query_property because it implicitely selects the whole object (all attributes of it), so adding the group_by would not produce the desired result because undesired columns are included in the non-aggregate part of the query.
However you can simply use a db_session.query([Person.country, ...]).group_by(...). So all you need to do is to add db_session property to the Person class along with query and then use it to create a desired query explicitely:
class Person(object):
session = db_session
query = db_session.query_property()
....
Related
I want to have a method which searches for data on the basis of status and (SellerId or BuyerId). The following method name doesnt work for me and gives me an error: No parameter available for part status SIMPLE_PROPERTY (1): [Is, Equals] NEVER.
What is to be corrected in the method signature? Seller and Buyer are two separate tables each having a field id.
findByStatusIsAndSellerIdOrStatusIsAndBuyerId(status, id)
findByStatusIsAndSellerIdOrStatusIsAndBuyerId(status, id)
expects 4 parameters for the four conditions.
You also should never use a derived query with AND and OR because precedence gets pretty confusing at best.
So pick a name that you like and use a #Query annotation to specify the query to use.
SELECT x FROM X x where x.status = :status and (x.seller.id = :id or x.buyer.id = :id) should be close to what you need.
I have models:
class Reference(models.Model):
name = models.CharField(max_length=50)
class Search(models.Model):
reference = models.ForeignKey(Reference)
update_time = models.DateTimeField(auto_now_add=True)
I have an instance of Reference and i need to get all last searches for the reference. Now i am doing it in this way:
record = Search.objects.filter(reference=reference)\
.aggregate(max_date=Max('update_time'))
if record:
update_time = record['max_date']
searches = reference.search_set.filter(update_time=self.update_time)
It is not a big deal to use 2 queries except the one but what if i need to get last searches for each reference on a page? I would have got 2x(count of references) queries and it would not be good.
I was trying to use this solution https://stackoverflow.com/a/9838438/293962 but it didn't work with filter by reference
You probably want to use the latest method.
From the docs, "Returns the latest object in the table, by date, using the field_name provided as the date field."
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#latest
so your query would be
Search.objects.filter(reference=reference).latest('update_time')
I implemented a snippet from someone in gist but I don't remember the user neither have the link.
A bit of context:
I have a model named Medicion that contains the register of mensuration of a machine, machines are created in a model instance of Equipo, Medicion instances have besides of a Foreign key to Equipo, a foreign key to Odometro, this model serves as a kind of clock or metre, that's why when I want to retrieve data (measurements aka instances of Medicion model) for a certain machine, I need to indicate the clock as well, otherwise it would retrieve me a lot of messy and unreadable data.
Here is my implementation:
First I retrieve the last dates:
ult_fechas_reg = Medicion.objects.values('odometro').annotate(max_fecha=Max('fecha')).order_by()
Then I instance an Q object:
mega_statement = Q() # This works as 'AND' Sql Statement
Then looping in every date retrieved in the queryset(annotation) and establishing the Q statement:
for r in ult_fechas_reg:
mega_statement |= (Q(odometro__exact=r['odometro']) & Q(fecha=r['max_fecha']))
Finally passed this mega statement to the queryset that pursues to retrieve the last record of a model filtered by two fields:
resultados = Medicion.objects.filter(mega_query).filter(
equipo=equipo,
odometro__in=lista_odometros).order_by('odometro', 'fecha') # lista_odometros is a python list containing pks of another model, don't worry about it.
Simple statement of the problem:
The ActiveRecord documentation shows that you can pass several values into the object find() method, as follows:
Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
My issue is that the objects returned are insensitive to the order of the values you pass in.
For instance, Person.find(1, 2, 6) returns exactly the same thing as Person.find(6, 1, 2).
Is there any way to make this kind of search order sensitive?
It feels like there should be a way to pass in an array of id and get an array of Person objects back in the same order...
Broader context, for those interested in reading on:
Really what I'm looking to do more generally, is find the "most viewed" Person objects for a given time period.
Here's what I've got:
#most_viewed = View.where('created_at < ?', Time.now - 1.week).group(:person_id).order('count_person_id desc').count('person_id').keys
This returns an (ordered!) array of id values for Person objects, in descending order of number of Views each Person has received in the last week.
My thinking was, if I could pass this array of ids into the Person.find method, then I'm home free! But maybe there's another way entirely to do it more easily. I'm open to all thoughts.
Thanks!
Instead of returning ids from your query you can return the views itself.
#most_viewed = View.where('created_at < ?', Time.now - 1.week)
.group(:person_id).order('count_person_id desc').includes(:person)
Now you have the persons ordered, but you just have to access them through #most_viewed variable.
#most_viewed.each do |view|
# do something with view.person
end
Edit:
If the solution above is not working for you, you can order the persons in this way
Person.find([6, 1, 2]).index_by(&:id).slice(*[6, 1, 2]).values
But the order is happening in ruby, not in the query.
Background
I'm faced with the following problem, relating to three tables
class_sectors table contains three categories of classes
classes table contains a list of classes students can attend
class_choices contains the first, second and third class choice of the student, for each sector. So for sector 1 Student_A has class_1 as first choihce, class_3 as second choice and class_10 as third choice for example, then for sector 2 he has another three choices, etc...
The class_choices table has these columns:
kp_choice_id | kf_personID | kf_sectorID | kf_classID | preference | assigned
I think the column names are self explanatory. preference is either 1, 2 or 3. And assigned is a boolean set to 1 once we have reviewed a student's choices and assigned them to a class.
Problem:
Writing an sql query that tells the students what class they are assigned to for each sector. If their class hasn't been assigned, it should default to show their first preference.
I have actually got this to work, but using two (very bloated??) sql queries as follows:
$choices = $db -> Q("SELECT
*, concat_ws(':', `kf_personID`, `kf_sectorID`) AS `concatids`
FROM
`class_choices`
WHERE
(`assigned` = '1')
GROUP BY
`concatids`
ORDER BY
`kf_personIDID` ASC,
`kf_sectorID` ASC;");
$choices2 = $db -> Q("SELECT
*, concat_ws(':', `kf_personID`, `kf_sectorID`) AS `concatids`
FROM
`class_choices`
WHERE
`preference` = '1'
GROUP BY
`concatids`
HAVING
`concatids` NOT IN (".iimplode($choices).")
ORDER BY
`kf_personID` ASC,
`kf_sectorID` ASC;");
if(is_array($choices2)){
$choices = array_merge($choices,$choices2);
}
Now $choices does have what I want.
But I'm sure there is a way to simplify this, merge the two SQL queries, and so it's a bit more lightweight.
Is there some kind of conditional SQL query that can do this???
Your solution uses two steps to enable you to filter the data as needed. Since you are generating a report, this is a pretty good approach even if it looks a bit more verbose than you might like.
The advantage of this approach is that it is much easier to debug and maintain, a big plus.
To improve the situation, you need to consider the data structure itself. When I look at the class_choices table, I see the following fields: kf_classID, preference, assigned which contain the key information.
For each class, the assigned field is either 0 (default) or 1 (when the class preference is assigned for the student). By default, the class with preference = 1 is the assigned one since you display it in the report when assigned=0 for all the student's class choices in a particular sector.
The data model could be improved by imposing a business rule as follows:
For preference=1 set the default value assigned=1. When the class selection process
takes place, and if the student gets assigned the 2nd or 3rd choice, then preference 1 is unassigned and the alternate choice assigned.
This means a bit more code in the application but it makes the reporting a bit easier.
The source of the difficulty is that the assignment process does not explicitly assign the 1st preference. It only updates assigned if the student cannot get the 1st choice.
In summary, your SQL is good and the improvements come from taking another look at the data model.
Hope this helps, and good luck with the work!
I've got records in my MySQL projects database that have several boolean flags to help me sort the data. I have 3 categories planning, landscape, and environmental and 4 classes (or subcategories) for each category; industrial, government, education, residential.
My goal is to use ColdFusion to create and store the project_id numbers in an array of some kind that will basically have the projects sorted by category and class. That way I can grab just the industrial projects in the planning category and construct a link to that project.
So, the first position in the array would be planning and inside that first position would be the 4 classes, then, within that would be all of the project_id numbers that returned true for those particular criteria.
The logic I'm looking to create goes like this...
Loop over the query result, if planning = true and industrial = true, place the project id # into the planning array inside the industrial array.
How can I use <cfloop> to loop over the list of project records, recognize the category and class flags and construct a clean and usable dataset? Can this be handles in the query in some way?
Figure out the desired data structure
look at your existing data structure
figure out the algorithm to translate from one to the other
You may cfloop the query, and use a large cfswitch (or large set of if-then-else) to figure out how you want to store the id in your desired data structure. Or if you can map the class/category name as a struct key, then it might be easier.
Or you may use cfoutput group="" if it helps (cfloop group="" is available on in CF10+)
Finally, maybe you don't even need the data structure, just use Query of Queries wherever you need.
You may be able to utilize the Underscore.cfc library for this. For example, you could use the filter function to extract an array of structs representing the query rows you want:
planningArray = _.filter(queryResult, function(row){
return (row.planning == true && row.industrial == true);
});