Class::DBI

2003-06-28 Thread Alex McLintock
I'm trying to understand Class::DBI

I'm reading the perldoc and TOny Bowden's article on perl.com

The problem I have is that I can't see how to build up more complicated 
queries such as

give me the first one hundred book titles

Now I am using MySQL which I don't think can limit the number of records 
returned... but I do have a title number so I could try to do something like

give me all the book titles where the titlenumber is  400 and the 
titlenumber  500

Unfortunately I can't see any way of doing this.

Anybody know if I have missed something?

I know I can use iterators but the perldoc say that Class::DBI iterators 
still suck up all the data into memory - which isn't really much use either.

Alex Mc

Available for java/perl/C++/web development in London, UK or nearby.
Apache FOP, Cocoon, Turbine, Struts,XSL:FO, XML, Tomcat, JSP
http://www.OWAL.co.uk/



Re: Class::DBI

2003-06-28 Thread Jason Clifford
On Sat, 28 Jun 2003, Alex McLintock wrote:

 The problem I have is that I can't see how to build up more complicated 
 queries such as
 
 give me the first one hundred book titles
 
 Now I am using MySQL which I don't think can limit the number of records 
 returned... but I do have a title number so I could try to do something like

Yes it can. From the manual:

SELECT * FROM table LIMIT 100;  # Retrieve first 100 rows

or

SELECT * FROM table LIMIT 5,10 # Rows 6-15

The syntax is LIMIT offset, max_rows_to_return

The MySQL manual is very good.

Jason Clifford
-- 
UKFSN.ORG   Finance Free Software while you surf the 'net
http://www.ukfsn.org/   Sign up now




Re: Class::DBI

2003-06-28 Thread Richard Clamp
On Sat, Jun 28, 2003 at 11:11:43AM +0100, Alex McLintock wrote:
 I'm trying to understand Class::DBI
 
 I'm reading the perldoc and TOny Bowden's article on perl.com
 
 The problem I have is that I can't see how to build up more complicated 
 queries such as
 
 give me the first one hundred book titles

There's the Cretrieve_from_sql method

  my @books = Book-retrieve_from_sql(id  $lastid ORDER BY id LIMIT 100);

Or, documented really nearby to that, Cadd_constructor

  __PACKAGE__-add_constructor( top100 = id  ? ORDER BY id LIMIT 100 );
  my @books = Book-top100(0);

Of course ordering by id probably isn't correct, but I'm sure you know
which column you really want to order on

-- 
Richard Clamp [EMAIL PROTECTED]



Re: Class::DBI::Join

2003-02-18 Thread Tony Bowden
On Mon, Feb 17, 2003 at 12:37:17PM +, Kate L Pugh wrote:
 I sent this question to the Class::DBI list last week, but didn't get
 any replies so I thought I'd ask here too.  Can anyone help me out?

I think the problem is that Class::DBI::Join is a Schwern-ism, that
no-one else knows much about, and he frequently disappears for months
at a time ...

My guess is that you don't actually want to be using Class::DBI::Join,
but using a combination of the has_many-with-optional-mapping-method
functionality of Class::DBI itself, with the has_many taking search
arguments.

Tony




Class::DBI::Join

2003-02-17 Thread Kate L Pugh
I sent this question to the Class::DBI list last week, but didn't get
any replies so I thought I'd ask here too.  Can anyone help me out?

Kake

- Forwarded message from Kate L Pugh [EMAIL PROTECTED] -

Hello.  I only started using Class::DBI last week so please don't
laugh if this is a stupid question.

I'm using Class::DBI::Join to relate dealers (of the art kind) to
categories.  So I've got a (many-to-many) table that relates dealer
IDs and category IDs, and a Categorisation class that subclasses
Class::DBI::Join so I can put dealers in categories and get them out
again.  The thing I need to add is support for the 'importance' column
in that table - some dealers are major dealers in a category, others
only occasionally stock items from that category.  Now as it stands I
can do:

  $category-add_dealer( dealer = $minor_dealer, importance = 2 );

and the information goes in the table, and I can get all the dealers in
a category with:

  @dealers = Categorisation-join( $category ); 

but I want to be able to do:

  @dealers = Categorisation-join( $category, importance = 1 );

to get just the major dealers, and I can't work out what the
recommended way to do this is.  I made a patch to Class::DBI::Join
(attached with tests) to make the line above Just Work, but I'm sure
I've missed something that makes this a bad idea.

Help?


Kake


diff -pubr Class-DBI-Join-0.03/lib/Class/DBI/Join.pm 
class-dbi-join/lib/Class/DBI/Join.pm
--- Class-DBI-Join-0.03/lib/Class/DBI/Join.pm   2002-04-17 16:10:16.0 +0100
+++ class-dbi-join/lib/Class/DBI/Join.pm2003-02-12 12:27:24.0 +
@@ -24,7 +24,8 @@ Class::DBI::Join - many-to-many relation
   # Given a join table like:
   # CREATE TABLE films_and_actors (
   # film_id INTEGER REFERENCES films,
-  # actor_idINTEGER REFERENCES actors
+  # actor_idINTEGER REFERENCES actors,
+  # as_name VARCHAR(80)
   # );
   # And assuming Film and Actor are Class::DBI subclasses.
   Roles-table('films_and_actors');
@@ -40,6 +41,7 @@ Class::DBI::Join - many-to-many relation
 
   my $btaste = Film-retrieve('Bad Taste');
   my @roles  = Roles-join($btaste);
+  my @alien_roles = Roles-join($btaste, as_name = '3rd Class Alien');
 
 =head1 DESCRIPTION
 
@@ -57,8 +59,17 @@ Class::DBI::Join adds the following meth
 
 =item Ijoin
 
+  # Find all roles in the film Bad Taste.
   my @roles = Roles-join($btaste);
 
+  # Or just those in which the actors are playing aliens.
+  my @alien_roles = Roles-join($btaste, as_name = '3rd Class Alien');
+
+The first argument to Cjoin should be the object whose relations you
+want to find. Any other arguments will be passed straight through to
+the Csearch method of LClass::DBI, so you can provide information
+here to construct additional WHERE clauses.
+
 =cut
 
 __PACKAGE__-set_sql('ManyToMany', SQL);
@@ -68,10 +79,10 @@ WHERE   %s = ?
 SQL
 
 sub join {
-my($class, $one) = @_;
+my($class, $one, @args) = @_;
 
 my $hasa_cols= __hasa_cols($class);
-return $class-search( $hasa_cols-{ref $one} = $one-id );
+return $class-search( $hasa_cols-{ref $one} = $one-id, @args );
 }
 
 
diff -pubr Class-DBI-Join-0.03/t/Join.t class-dbi-join/t/Join.t
--- Class-DBI-Join-0.03/t/Join.t2002-04-16 21:09:03.0 +0100
+++ class-dbi-join/t/Join.t 2003-02-12 12:20:39.0 +
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 
-use Test::More tests = 11;
+use Test::More tests = 13;
 require_ok('Class::DBI::Join');
 
 use lib qw(t/lib/);
@@ -60,6 +60,12 @@ my $btaste = Film-retrieve('Bad Taste')
 my @bt_roles = Roles-join($btaste);
 is( @bt_roles, 6 );
 
+my @aliens = Roles-join($btaste, as_name = '3rd Class Alien');
+is( @aliens, 2, 'join works with extra where clause' );
+my @alien_actors = sort map { $_-actor-name } @aliens;
+is_deeply( \@alien_actors, ['Craig Smith (I)', 'Terry Potter'],
+  '...returns the right things too' );
+
 my $pj = Actor-retrieve('Peter Jackson');
 my @pj_roles = Roles-join($pj);
 is( @pj_roles, 4 );


- End forwarded message -




Class::DBI ponderings

2003-02-07 Thread Simon Batistoni
We're currently considering shifting our core mod-perl system here at
work to use Class::DBI, since many of the classes we have already are
pretty much reinventing its wheel, and we currently have the time and
space to make a few structural changes before the next development
onslaught begins.

However, I've run up against an interesting little problem. Currently,
our database (mysql) has two users for cgi access - one called
nobody, which has read-access only to the database, and one called,
well, never you mind, which has full access.

Scripts that are just dealing with front-end presentation use the
nobody user, whilst the CMS side of the system uses the full-access
user.

The problem with Class::DBI is that its connections persist across
classes, and since we want to do as much caching of DB connections etc
as possible, the scheme of having two database users gets a bit
tricky.

So, two major questions:

1) The old nobody/full-access duality is an old piece of history,
and rose out of the general principle that it's best to only have a
full-access user connected when necessary. However, it came about when
there was very little caching in the system, and has now become a
pain where once it wasn't.

What do other people do? Just connect using a user with full
privileges, regardless of the script's task? I can't see huge security
disadvantages in this, particularly as such users are locked down to
only take connections from localhost. Still, opinions would be nice.

2) If people do connect with different users dependent on task, how do
you keep your cached DB handles straight? I'd thought of subclassing
admin-related tasks into separate packages, so that they always get an
Object::Foo::Admin-created DB handle, but this seems unwieldy in the
extreme.


Unless anyone can convince me that it's a bad idea, I think having a
single full access user is going to be the most attractive solution
here, but opinions welcome.




Re: Class::DBI ponderings

2003-02-07 Thread Simon Wilcox
On Fri, 2003-02-07 at 17:34, Simon Batistoni wrote:
 We're currently considering shifting our core mod-perl system here at
 work to use Class::DBI, since many of the classes we have already are
 pretty much reinventing its wheel, and we currently have the time and
 space to make a few structural changes before the next development
 onslaught begins.
 
 However, I've run up against an interesting little problem. Currently,
 our database (mysql) has two users for cgi access - one called
 nobody, which has read-access only to the database, and one called,
 well, never you mind, which has full access.
 
 Scripts that are just dealing with front-end presentation use the
 nobody user, whilst the CMS side of the system uses the full-access
 user.
 
 The problem with Class::DBI is that its connections persist across
 classes, and since we want to do as much caching of DB connections etc
 as possible, the scheme of having two database users gets a bit
 tricky.
 
 So, two major questions:
 
 1) The old nobody/full-access duality is an old piece of history,
 and rose out of the general principle that it's best to only have a
 full-access user connected when necessary. However, it came about when
 there was very little caching in the system, and has now become a
 pain where once it wasn't.
 
 What do other people do? Just connect using a user with full
 privileges, regardless of the script's task? I can't see huge security
 disadvantages in this, particularly as such users are locked down to
 only take connections from localhost. Still, opinions would be nice.
 
 2) If people do connect with different users dependent on task, how do
 you keep your cached DB handles straight? I'd thought of subclassing
 admin-related tasks into separate packages, so that they always get an
 Object::Foo::Admin-created DB handle, but this seems unwieldy in the
 extreme.
 
 
 Unless anyone can convince me that it's a bad idea, I think having a
 single full access user is going to be the most attractive solution
 here, but opinions welcome.

I tend to use a single user with read/write access and do the security
at the application level. They still don't have full access, just
enough to do what they need to do.

Assuming you have subclassed Class::DBI and then subclassed again for
each of your objects (the recommended way) then you can overload db_Main
to return the right handle.

Basically db_Main is called every time Class::DBI needs a database
connection. I overload it so that I can establish the connection
dynamically at run time from a config file but there is no reason why
you can't have it look at some variable (e.g. the users id or the
attempted action) and return a read only or read/write connection.

HTH,

Simon.




Re: Class::DBI ponderings

2003-02-07 Thread Tony Bowden
On Fri, Feb 07, 2003 at 05:34:39PM +, Simon Batistoni wrote:
 What do other people do? Just connect using a user with full
 privileges, regardless of the script's task? I can't see huge security
 disadvantages in this, particularly as such users are locked down to
 only take connections from localhost. Still, opinions would be nice.

This is what we generally do ... but if you want to keep the two users:

 2) If people do connect with different users dependent on task, how do
 you keep your cached DB handles straight? I'd thought of subclassing
 admin-related tasks into separate packages, so that they always get an
 Object::Foo::Admin-created DB handle, but this seems unwieldy in the
 extreme.

In your main Class::DBI subclass, which will presumably be the
superclass for each of your 'table' classes, you can override the
db_Main method.

Unfortunately, because of the way Ima::DBI just throws this method into
your namespace, you can't do this directly.

You can create another level of subclassing so that you can properly
override the method:

in file My/DBI.pm
package My::DBI::Base;

use base 'Class::DBI';
__PACKAGE__-set_db();

package My::DBI;

use base My::DBI::Base;

sub db_Main {
my $class = shift;
if ($class-different_privs) {
return Ima::DBI-connect_cached('other connect string');
} 
return $class-SUPER::db_Main;
}

1;

Or, you can grab a copy of the method after it's been created, and mess
about with it:

in file My/DBI.pm
package My::DBI;

use base 'Class::DBI';
__PACKAGE__-set_db();

{
no warnings 'redefine';
*db_Orig = \db_Main;
*db_Mail = sub {
my $class = shift;
if ($class-different_privs) {
return Ima::DBI-connect_cached('other connect 
string');
} 
$class-db_Orig;
}
}


It's kinda tricky, and should really be a lot nicer, but it's fairly
flexible (we've used it to have lots of different databases with the
same schema, and the correct one gets picked depending on certain
circumstances (what user you're logged in as, which vhost you're using, 
what the path name is, whatever)...

If you've any more questions, the Class::DBI mailing list has a few
people who've done things like this.

Thanks,

Tony







Re: Class::DBI ponderings

2003-02-07 Thread Tony Bowden
On Fri, Feb 07, 2003 at 08:00:07PM +, Shevek wrote:
 On reading the code, this is sufficient. Don't call set_db at all. This is 
 pretty close to the architecture I used to use: Each class was responsible 
 for providing an appropriate DB handle on demand. If you don't call 
 set_db, and just use connect_cached, then much of what followed in Tony's 
 mail may be simplified.

yes, this is indeed true. I hadn't actually thought of that.

I need to put together a good write-up on all the different approaches
to this.

Tony




Re: Class::DBI trickery

2002-11-13 Thread Tony Bowden
On Tue, Nov 12, 2002 at 07:09:28PM +, Michael Styer wrote:
 I've just been introduced to Class::DBI. While I like it lots in general,
 it seems like it's missing one obvious featur

 So what I wanted to be able to do is this:
 my $survey = Survey-retrieve($survey_id);
 $survey-add_question(\%question_data);
 where the 'add_question' method is created automagically 

Very interesting approach...

 rather than having to do this:
 my $survey = Survey-retrieve($survey_id);
 my $question = Survey::Question-create(\%question_data);
 $question-survey($survey);

Technically, if you were doing it this way, you should probably do:
 my $survey = Survey-retrieve($survey_id);
 my $qn = Survey::Question-create({ %question_data, survey = $survey });

as some databases might complain that you're violating the schema at the
initial create (as your foreign key is null until the next statement).

But I definitely like the first approach.

 I don't like this because I have to hard-code the name of the child
 class somewhere else besides the has_many initialization statement,
 and that feels messy. It also just feels backwards.

I'd be interested as to why you think it feels backwards...

 I've overridden 'has_many' with a version that does what I want, but my
 question is, am I missing a trick? Is there a standard idiom for doing
 things more or less the way I wanted to within the existing Class::DBI
 framework?

I don't think so. Would you mind submitting a patch? 

Thanks,

Tony





Re: Class::DBI trickery

2002-11-13 Thread Michael Styer
On Wed, 13 Nov 2002, Tony Bowden wrote:

  I don't like this because I have to hard-code the name of the child
  class somewhere else besides the has_many initialization statement,
  and that feels messy. It also just feels backwards.
 
 I'd be interested as to why you think it feels backwards...

Well, if I have a parent object which has a collection of child objects of
some class (or collections of objects of different classes), it seems like
the parent should know what it has to do to add a child to (one of) its
collection(s), as long as you give it the right data for the new child
object. As it is, the parent can retrive objects it contains but can't add
more, so the programmer has to know what class the child objects are in
order to add more of them.

  I've overridden 'has_many' with a version that does what I want, but my
  question is, am I missing a trick? Is there a standard idiom for doing
  things more or less the way I wanted to within the existing Class::DBI
  framework?
 
 I don't think so. Would you mind submitting a patch? 

Sure. I'll send it off-list unless anyone else is interested.

While we're on the subject of Class::DBI, I'm getting deep recursion in
Class::DBI::DESTROY. It's the call to $self-id at line 501 that seems to
set it off, but if I take out my triggers it works. Probably something I'm
doing wrong and I've got the perl debugger on the task, but if anyone has
any suggestions about where to look I'd love to hear them.

Thanks.

michael






Re: Class::DBI trickery

2002-11-13 Thread Tony Bowden
On Wed, Nov 13, 2002 at 12:36:54PM +, Michael Styer wrote:
  I'd be interested as to why you think it feels backwards...
 Well, if I have a parent object which has a collection of child objects of
 some class (or collections of objects of different classes), it seems like
 the parent should know what it has to do to add a child to (one of) its
 collection(s), as long as you give it the right data for the new child
 object. As it is, the parent can retrive objects it contains but can't add
 more, so the programmer has to know what class the child objects are in
 order to add more of them.

Gotcha. That makes perfect sense! :)

  I don't think so. Would you mind submitting a patch? 
 Sure. I'll send it off-list unless anyone else is interested.

Great.

 While we're on the subject of Class::DBI, I'm getting deep recursion in
 Class::DBI::DESTROY. It's the call to $self-id at line 501 that seems to
 set it off, but if I take out my triggers it works. Probably something I'm
 doing wrong and I've got the perl debugger on the task, but if anyone has
 any suggestions about where to look I'd love to hear them.

First suggestion is to update to 0.90_05 (available from
www.class-dbi.com), which in the next day or two is going to become
0.90. It gives better error messages for some deep recursion cases that
might help you track the problem better...

If that doesn't help, then feel free to send me your code off-list (or
to the CDBI mailing list), and I'll see if I can spot anything.

Thanks,

Tony




Class::DBI trickery

2002-11-12 Thread Michael Styer
I've just been introduced to Class::DBI. While I like it lots in general,
it seems like it's missing one obvious feature, but being new to the
module I think it's more likely I'm missing the point than Class::DBI is
missing a feature.

Here's the situation.

I set up two classes, like so:

package Survey;

use base 'My::DBI';
use strict;

__PACKAGE__-table('surveys');
__PACKAGE__-columns(Primary = 'survey_id');
__PACKAGE__-columns(All = qw(title text));
__PACKAGE__-has_many('questions', Survey::Question = 'survey_id');

package Survey::Question;

use base 'My::DBI';
use strict;

__PACKAGE__-table('questions');
__PACKAGE__-columns(Primary = 'question_id');
__PACKAGE__-columns(All = qw(title text));

(the My::DBI class has all the db connection details in it and is a
subclass of Class::DBI, so I don't have to keep that in the
sub-classes.  also, there's a class for answers as well but let's
leave that out for clarity.)

So what I wanted to be able to do is this:

my $survey = Survey-retrieve($survey_id);
## get question data from somewhere
$survey-add_question(\%question_data);

where the 'add_question' method is created automagically in the same way
the 'questions' method is created by Class::DBI, rather than having to do
this:

my $survey = Survey-retrieve($survey_id);
## get question data
my $question = Survey::Question-create(\%question_data);
$question-survey($survey);

which is, as far as I can tell, the only way to add a child object to a
parent object. (child being the 'hasa' end of the relationship and parent
being the 'has_many' end.) I don't like this because I have to hard-code
the name of the child class somewhere else besides the has_many
initialization statement, and that feels messy. It also just feels
backwards.

I've overridden 'has_many' with a version that does what I want, but my
question is, am I missing a trick? Is there a standard idiom for doing
things more or less the way I wanted to within the existing Class::DBI
framework?

michael