I have been struggling with an issue for a couple of days. I am sharing a Content Provider with two different apps (app A and app B). All the stuff regarding DB creation and Content Provider management is done by app A. App B just accesses it using the corresponding Authorities and a Content Provider Client.
ContentProviderClient myCPClient = this.miContext.getContentResolver().acquireContentProviderClient(this.miUri);
The problem comes up when trying to query the database in a more complex way, i.e. using some key words like GROUP BY, HAVING, etc. I need to get unique references according to one specific column (I want to use GROUP BY), and I have found out that there is no rawQuery() method for a ContentProviderClient, but a simplified query() method (compared to the one available in the class SQLiteDatabase, which allows to formulate proper MySQL commands).
I have checked this answer, but since my ContentProvider is accessed from a different app, I do not have any class like MyContentProvider.
To sum up, is there any way to make a proper query (like rawQuery()) to a ContentProvider which was generated by a different app?
I have finally got to a solution which is rather simple and sensible. I got a very good explanation about Content Providers and Content Resolvers. The latter is used to access the former, which means that they can not control what is in the provider, but get data from them. This means that you can not make a Content Provider Client to use a rawQuery() if it is not implemented (override) in the query() method of the corresponding ContentProvider.
To work around my problem, I have used a flag in my provider client and modify my content provider to read it so I can make use of GROUP BY. I just wanted to get unique references from the database according to a particular column.
Here it is the solution, which is not a very clean one, but it works quite well.
For the ContentProviderClient,
ContentProviderClient myCPClient = this.miContext.getContentResolver().acquireContentProviderClient(this.miUri);
//I declare some variables for the query
//'selection' will get all the rows whose "_id" is greater than 0, i.e. all the rows
String selection = BaseDatosParam.Tabla._ID + ">?";
String[] selectionArgs = {"0"};
//'groupBy' is not formatted in any particular way. I just need it to contain the pattern "GROUP BY"
String groupBy = "GROUP BY" + BaseDatosParam.Tabla.REF;
//the last field of the query corresponds to 'sortOrder', but I
Cursor c = myCPClient.query(Uri.parse(miUri.toString()),
projection, selection, selectionArgs, groupBy);
In the ContentProvider,
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
String where = selection;
String groupBy = null;
SQLiteDatabase db = this.miBDManager.getWritableDatabase();
//We just check out whether 'sortOrder' includes the pattern "GROUP BY", otherwise that field will remain null
Pattern myPat = Pattern.compile("GROUP BY");
Matcher myMat = myPat.matcher(sortOrder);
if (myMat.find())
groupBy = myMat.replaceFirst("");
Cursor c = db.query(BaseDatosParam.Tabla.NOMBRE_TABLA, projection, where, selectionArgs, groupBy, null, null);
c.setNotificationUri(this.getContext().getContentResolver(), uri);
return c;
}
Regards,
Related
Is there a way to get the columns of a table in the order that they are defined within the database in activejdbc? I tried this:
User u = new User(); // User extends org.javalite.activejdbc.Model
Map<String, ColumnMetadata> columns = u.getMetaModel().getColumnMetadata();
However, the returned map has the columns in alphabetical order, not the order that is defined in the table.
I'm using MySQL if that matters.
It uses standard JDBC metadata calls, please see here: Registry#getColumns.
However this is returned from the JDBC driver, it is added to a Map in the code. As you know, maps are not ordered in any way. Not sure why you would need to get the columns in the same order as they are defined in the database, or if this is even possible.
If you really want this, you can drop to a DB level:
Connection con = Base.connection();
// get metadata from the connection any way you want
This way, you can get whatever driver can provide (your mileage is will be limited by the driver implementation)
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.
I'm learning sqlalchemy and not sure if I grasp it fully yet(I'm more used to writing queries by hand but I like the idea of abstracting the queries and getting objects). I'm going through the tutorial and trying to apply it to my code and ran into this part when defining a model:
def __repr__(self):
return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
Its useful because I can just search for a username and get only the info about the user that I want but is there a way to either have multiple of these type of views that I can call? or am I using it wrong and should be writing a specific query for getting different data for different views?
Some context to why I'm asking my site has different templates, and most pages will just need the usersname, first/last name but some pages will require things like twitter or Facebook urls(also fields in the model).
First of all, __repr__ is not a view, so if you have a simple model User with defined columns, and you query for a User, all the columns will get loaded from the database, and not only those used in __repr__.
Lets take model Book (from the example refered to later) as a basis:
class Book(Base):
book_id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
summary = Column(String(2000))
excerpt = Column(Text)
photo = Column(Binary)
The first option to skip loading some columns is to use Deferred Column Loading:
class Book(Base):
# ...
excerpt = deferred(Column(Text))
photo = deferred(Column(Binary))
In this case when you execute query session.query(Book).get(1), the photo and excerpt columns will not be loaded until accessed from the code, at which point another query against the database will be executed to load the missing data.
But if you know before you query for the Book that you need the column photo immediately, you can still override the deferred behavior with undefer option: query = session.query(Book).options(undefer('photo')).get(1).
Basically, the suggestion here is to defer all the columns (in your case: except username, password etc) and in each use case (view) override with undefer those you know you need for that particular view. Please also see the group parameter of deferred, so that you can group the attributes by use case (view).
Another way would be to query only some columns, but in this case you are getting the tuple instance instead of the model instance (in your case User), so it is potentially OK for form filling, but not so good for model validation: session.query(Book.id, Book.title).all()
When you query an EntitySet property on a model object in Linq-to-SQL, it returns all rows from the entityset and does any further querying client-side.
This is confirmed in a few places online and I've observed the behavior myself. The EntitySet does not implement IQueryable.
What I've had to do is convert code like:
var myChild = ... ;
// Where clause performed client-side.
var query = myChild.Parents().Where(...) ;
to:
var myChild = ... ;
// Where clause performed in DB and only minimal set of rows returned.
var query = MyDataContext.Parents().Where(p => p.Child() == myChild) ;
Does anyone know a better solution?
A secondary question: is this fixed in the Entity Framework?
An EntitySet is just a collection of entities. It implements IEnumerable, not IQueryable. The Active Record pattern specifies that entities be directly responsible for their own persistence. OR mapper entities don't have any direct knowledge of the persistence layer. OR Mappers place this responsibility, along with Unit Of Work, and Identity Map responsibilities into the Data Context. So if you need to query the data source, you gotta use the context (or a Table object). To change this would bend the patterns in use.
I had a similar problem: How can I make this SelectMany use a join. After messing with LINQPad for a good amount of time I found a decent workaround. The key is to push the EntitySet you are looking at inside a SelectMany, Select, Where, etc. Once it's inside that it becomes an Expression and then the provider can turn it into a proper query.
Using your example try this:
var query = from c in Children
where c == myChild
from p in c.Parents
where p.Age > 35
select p;
I'm not able to 100% verify this query as I don't know the rest of your model. But the first two lines of the query cause the rest of it to become an Expression that the provider turns into a join. This does work with my own example that is on the question linked to above.
This code is a no-go
var errors = (from error in db.ELMAH_Error
select new
{
error.Application,
error.Host,
error.Type,
error.Source,
error.Message,
error.User,
error.StatusCode,
error.TimeUtc
}).ToList();
return View(errors);
as it results in a 'requires a model of type IEnumerable' error. The following code of course works fine, but selects all the columns, some of which I'm simply not interested in:
var errors = (from error in db.ELMAH_Error
select error).ToList();
return View(errors);
I'm brand spanking new to MVC2 + L2E, so maybe I'm just not thinking in the right mindset yet, but this seems counter-intuitive. Is there an easy way to select a limited number of columns, or is this just part of using an ORM?
This isn't an issue with L2E / L2S, but rather with MVC strongly-typed views.
I'm betting it's not just "requires a model of type IEnumerable", but "a model of type IEnumerable<Something>". If you have a strongly-typed view bound to the type of your errors table, then trying to pass it the anonymous type created by your query (via select new {...}) will cause a type mismatch.
You could either create a new type which contains only the rows that you want, and use that as your view page Model, or you could just do the full select and then limit the columns in your ASP code.