From: <sub...@co...> - 2005-08-01 05:55:36
|
Author: ianb Date: 2005-08-01 05:55:31 +0000 (Mon, 01 Aug 2005) New Revision: 874 Modified: trunk/SQLObject/docs/FAQ.txt Log: Removed includes from examples Modified: trunk/SQLObject/docs/FAQ.txt =================================================================== --- trunk/SQLObject/docs/FAQ.txt 2005-08-01 05:54:48 UTC (rev 873) +++ trunk/SQLObject/docs/FAQ.txt 2005-08-01 05:55:31 UTC (rev 874) @@ -23,9 +23,13 @@ Simple ~~~~~~ -.. raw:: html - :file: ../examples/snippets/leftjoin-simple.html +:: + for customer in Customer.select(): + print customer.firstName, customer.lastName + for contact in customer.contacts: + print ' ', contact.phoneNumber + The effect is the same as the left join -- you get all the customers, and you get all their contacts. The problem, however, is that you will be executing more queries -- a query for each customer to fetch @@ -38,10 +42,15 @@ Efficient ~~~~~~~~~ -Lets say you really don't want to do all those queries. Okay, fine: +Lets say you really don't want to do all those queries. Okay, fine:: -.. raw:: html - :file: ../examples/snippets/leftjoin-more.html + custContacts = {} + for contact in Contact.select(): + custContacts.setdefault(contact.customerID, []).append(contact) + for customer in Customer.select(): + print customer.firstName, customer.lastName + for contact in custContacts.get(customer.id, []): + print ' ', contact.phoneNumber This way there will only be at most two queries. It's a little more crude, but this is an optimization, and optimizations often look less @@ -49,10 +58,17 @@ But, say you don't want to get everyone, just some group of people (presumably a large enough group that you still need this -optimization): +optimization):: -.. raw:: html - :file: ../examples/snippets/leftjoin-more-query.html + query = Customer.q.firstName.startswith('J') + custContacts = {} + for contact in Contact.select(AND(Contact.q.customerID == Customer.q.id, + query)): + custContacts.setdefault(contact.customerID, []).append(contact) + for customer in Customer.select(query): + print customer.firstName, customer.lastName + for contact in custContacts.get(customer.id, []): + print ' ', contact.phoneNumber SQL-wise ~~~~~~~~ @@ -75,28 +91,49 @@ you can't do with SQLObject classes. However, some form of inheritance is possible. -One way of using this is to create local conventions. Perhaps: +One way of using this is to create local conventions. Perhaps:: -.. raw:: html - :file: ../examples/snippets/site-sqlobject.html + class SiteSQLObject(SQLObject): + _connection = DBConnection.MySQLConnection(user='test', db='test') + _style = MixedCaseStyle() + # And maybe you want a list of the columns, to autogenerate + # forms from: + def columns(self): + return [col.name for col in self._columns] + Since SQLObject doesn't have a firm introspection mechanism (at least not yet) the example shows the beginnings of a bit of ad hoc introspection (in this case exposing the ``_columns`` attribute in a more pleasing/public interface). However, this doesn't relate to *database* inheritance at all, since -we didn't define any columns. What if we do? +we didn't define any columns. What if we do? :: -.. raw:: html - :file: ../examples/snippets/inheritance.html + class Person(SQLObject): + firstName = StringCol() + lastName = StringCol() + class Employee(Person): + position = StringCol() + Unfortunately, the resultant schema probably doesn't look like what -you might have wanted: +you might have wanted:: -.. raw:: html - :file: ../examples/snippets/inheritance-schema.html + CREATE TABLE person ( + id INT PRIMARY KEY, + first_name TEXT, + last_name TEXT + ); + CREATE TABLE employee ( + id INT PRIMARY KEY + first_name TEXT, + last_name TEXT, + position TEXT + ) + + All the columns from ``person`` are just repeated in the ``employee`` table. What's more, an ID for a Person is distinct from an ID for an employee, so for instance you must choose ``ForeignKey("Person")`` or @@ -108,16 +145,38 @@ two. Of course, you can have that, just create the appropriate classes/tables -- but it will appear as two distinct classes, and you'd have to do something like ``Person(1).employee.position``. Of -course, you can always create the necessary shortcuts, like: +course, you can always create the necessary shortcuts, like:: -.. raw:: html - :file: ../examples/snippets/inheritance-faked.html + class Person(SQLObject): + firstName = StringCol() + lastName = StringCol() -It's not the most elegant setup, but it's functional and flexible. -There are no plans for further support for inheritance (especially -since the composition of multiple classes is usually a better solution -anyway). + def _get_employee(self): + value = Employee.selectBy(person=self) + if value: + return value[0] + else: + raise AttributeError, '%r is not an employee' % self + def _get_isEmployee(self): + value = Employee.selectBy(person=self) + # turn into a bool: + return not not value + def _set_isEmployee(self, value): + if value: + # Make sure we are an employee... + if not self.isEmployee: + Empoyee.new(person=self, position=None) + else: + if self.isEmployee: + self.employee.destroySelf() + def _get_position(self): + return self.employee.position + def _set_position(self, value): + self.employee.position = value + class Employee(SQLObject): + person = ForeignKey('Person') + position = StringCol() There is also another kind of inheritance. See Inheritance.html_ @@ -128,18 +187,44 @@ ----------------------------- A composite attribute is an attribute formed from two columns. For -example: +example:: -.. raw:: html - :file: ../examples/snippets/composite-schema.html + CREATE TABLE invoice_item ( + id INT PRIMARY KEY, + amount NUMERIC(10, 2), + currency CHAR(3) + ); Now, you'll probably want to deal with one amount/currency value, instead of two columns. SQLObject doesn't directly support this, but -it's easy (and encouraged) to do it on your own: +it's easy (and encouraged) to do it on your own:: -.. raw:: html - :file: ../examples/snippets/composite.html + class InvoiceItem(SQLObject): + amount = Currency() + currency = StringChar(length=3) + def _get_price(self): + return Price(self.amount, self.currency) + def _set_price(self, price): + self.amount = price.amount + self.currency = price.currency + + class Price(object): + def __init__(self, amount, currency): + self._amount = amount + self._currency = currency + + def _get_amount(self): + return self._amount + amount = property(_get_amount) + + def _get_currency(self): + return self._currency + currency = property(_get_currency) + + def __repr__(self): + return '<Price: %s %s>' % (self.amount, self.currency) + You'll note we go to some trouble to make sure that ``Price`` is an immutable object. This is important, because if ``Price`` wasn't and someone changed an attribute, the containing ``InvoiceItem`` instance @@ -149,11 +234,40 @@ *Value Object*, that can be used similar to how an integer or string is used. -You could also use a mutable composite class: +You could also use a mutable composite class:: -.. raw:: html - :file: ../examples/snippets/composite-mutable.html + class Address(SQLObject): + street = StringCol() + city = StringCol() + state = StringCol(length=2) + latitude = FloatCol() + longitude = FloatCol() + + def _init(self, id): + SQLObject._init(self, id) + self._coords = SOCoords(self) + + def _get_coords(self): + return self._coords + + class SOCoords(object): + def __init__(self, so): + self._so = so + + def _get_latitude(self): + return self._so.latitude + def _set_latitude(self, value): + self._so.latitude = value + latitude = property(_get_latitude, set_latitude) + + def _get_longitude(self): + return self._so.longitude + def _set_longitude(self, value): + self._so.longitude = value + longitude = property(_get_longitude, set_longitude) + + Pretty much a proxy, really, but ``SOCoords`` could contain other logic, could interact with non-SQLObject-based latitude/longitude values, or could be used among several objects that have @@ -209,19 +323,22 @@ any object in the column; the column, naturally, pickles the object upon assignment and unpickles it upon retrieving the data from the DB. -Another possible way to keep binary data in a database is by using encoding. -Base 64 is a good encoding, reasonably compact but also safe. As -an example, imagine you want to store images in the database: +Another possible way to keep binary data in a database is by using +encoding. Base 64 is a good encoding, reasonably compact but also +safe. As an example, imagine you want to store images in the +database:: -.. raw:: html - :file: ../examples/snippets/image-binary.html + class Image(SQLObject): -SQLite does not respect backslash quoting, so for instance ``\n`` is -not interpreted as a newline. For the moment there's no resolution, -and this will mess up your base64-encoded values. As a workaround: + data = StringCol() + height = IntCol() + width = IntCol() + + def _set_data(self, value): + self._SO_set_data(value.encode('base64')) -.. raw:: html - :file: ../examples/snippets/image-binary-sqlite.html + def _get_data(self, value): + return self._SO_get_data().decode('base64') Reloading Modules |