Primary Key con dos campos

239 views
Skip to first unread message

nicolás rosbaco

unread,
Feb 18, 2012, 5:36:13 PM2/18/12
to web2py-...@googlegroups.com
Hola gente tengo una tabla en la que necesito que dos campos operen como claves primarias...

tengo los campos: edición y nombre. Necesito que la combinación no se repita

puedo tener 2011-> tiburones y 2012 -> tiburones... si opto por nique=True en cualquiera de los campos (individualmente) no me sirve

Tengo cierta urgencia con esto así que se agradece cualquier soplido

gracias

--
"En un país colonial las oligarquías son las dueñas de los diccionarios" (John William Cooke)

Alvaro Lizama

unread,
Feb 18, 2012, 5:37:23 PM2/18/12
to web2py-...@googlegroups.com
Regresate a estudiar sobre base de datos y luego vienes a preguntar.

--
Alvaro Lizama
http://alvarolizama.net

nicolás rosbaco

unread,
Feb 18, 2012, 5:50:17 PM2/18/12
to web2py-...@googlegroups.com
no seas mala onda. Con MySQL no tengo problemas en definir el subconjunto de campos de la tabla que yo quiera como clave para la tabla...

No se como hacerlo con el DAL del web2py...

PD: que feo estas respuestas... papá noél no te va a traer una carajo si seguís así

Alan Etkin

unread,
Feb 19, 2012, 7:55:55 AM2/19/12
to web2py-usuarios
Interesante el problema, no estoy seguro de que web2py soporte ese
tipo de estructura de la tabla. Por lo menos en la sección del manual
sobre "keyed tables", dice que si la tabla es pre-existente y contiene
campos clave combindados, se puede acceder pero bajo condiciones
especiales (si te interesa la sección del libro es 6.13)

Una forma de resolver el asunto sería tener algo así:
-Crear un nuevo campo en la tabla que combine los campos edición y
nombre y le especificás la propiedad unique
-Crear una función que tome el registro y devuelva una concatenación
de campos.
-Asociar la función al campo combinado con el atributo compute

De todas formas deberías usar un campo id primario, se trata de una
solución parcial.

Marco Mansilla

unread,
Feb 19, 2012, 9:23:54 AM2/19/12
to web2py-...@googlegroups.com
IS_NOT_IN_DB() no hace algo similar?, por que las claves deben ser
unicas, claro en la practica las tablas solo pueden tener una clave
primaria, sin embargo este atributo hace que otro campo se pueda
comportar de manera similar... tienes la unicidad asegurada... si es
que no probaste, proba y nos cuentas... puede que me equivoque.

nicolás rosbaco

unread,
Feb 19, 2012, 10:51:14 PM2/19/12
to web2py-...@googlegroups.com
hola, y desde ya gracias por la atención, te comento:

intenté lo que me sugerís. tengo esto:

db.define_table('equipos',
    Field('nombre', requires = [IS_NOT_EMPTY()], label = 'Nombre del equipo'),
    Field('propietario',   'reference auth_user', required=True),
    Field('edicion', 'reference ediciones', required=True),
    Field('nombreclave', unique=True, compute=lambda r: str(r['edicion'])+r['nombre']))

resulta que puedo insertar sin problemas dos registros con la "combinación prohibida"

equipos.id equipos.nombreequipos.propietario equipos.edicionequipos.nombreclave
6Los Pehuenes3 11Los Pehuenes
7 Tiburones1011Tiburones
8Tiburones1 11Tiburones


te paso acá la captura desde el appadmin de web2py, como podés ver nombre clave se repite (estoy mas perdido que turco en la neblina)

bueno seguimos la charla... gracias

PD: estoy usando sqlite

Marco Mansilla

unread,
Feb 19, 2012, 11:04:33 PM2/19/12
to web2py-...@googlegroups.com
Solo por curiosidad... probaste usar IS_NOT_IN_DB(db,
db.equipos.nombreclave) en lugar de unique?...

> hola, y desde ya gracias por la atención, te comento:
>
> intenté lo que me sugerís. tengo esto:
>
> db.define_table('equipos',
> Field('nombre', requires = [IS_NOT_EMPTY()], label = 'Nombre del
> equipo'),
> Field('propietario', 'reference auth_user', required=True),
> Field('edicion', 'reference ediciones', required=True),
> Field('nombreclave', unique=True, compute=lambda r:
> str(r['edicion'])+r['nombre']))
>
> resulta que puedo insertar sin problemas dos registros con la
> "combinación prohibida"
>

> equipos.id<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=equipos.id>
> equipos.nombre<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=equipos.nombre>
> equipos.propietario<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=equipos.propietario>
> equipos.edicion<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=equipos.edicion>
> equipos.nombreclave<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=equipos.nombreclave>
> 6
> <http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/equipos/6>Los
> Pehuenes3<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/auth_user/3>
> 1
> <http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/ediciones/1>1Los
> Pehuenes7<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/equipos/7>
> Tiburones10<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/auth_user/10>
> 1
> <http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/ediciones/1>
> 1Tiburones8<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/equipos/8>
> Tiburones1<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/auth_user/1>
> 1
> <http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/ediciones/1>

nicolás rosbaco

unread,
Feb 20, 2012, 12:52:28 AM2/20/12
to web2py-...@googlegroups.com
ahora lo pruebo... pero primero te pregunto:
esto es lo que creo:
si uso unique=True esto opera a nivel de la base de datos y no de los validadores (javascript); en cambio si uso requires = IS_NO_IN_DB..... estaría programando los validadores de js.... ¿es correcto?

aclaro más aún: debería hacer ambas cosas... creo yo. Uno es el modelo de datos (la arquitectura de mi DB) y la segunda mi esquema de validadores JS....

espero y corrección en esto

gracias

nicolás rosbaco

unread,
Feb 20, 2012, 12:55:47 AM2/20/12
to web2py-...@googlegroups.com
acabo de probar, lo deje así:


db.define_table('equipos',
    Field('nombre', requires = [IS_NOT_EMPTY()], label = 'Nombre del equipo'),
    Field('propietario',   'reference auth_user', required=True),
    Field('edicion', 'reference ediciones', required=True),
    Field('nombreclave', unique=True, compute=lambda r: str(r['edicion'])+r['nombre'], requires=IS_NOT_IN_DB(db,'equipos.nombreclave', error_message='prueba repetida')))


a pesar de esto me permite insertar dos campos iguales:
10 Tiburones111Tiburones
11Tiburones13 11Tiburones

Alan Etkin

unread,
Feb 20, 2012, 8:00:24 AM2/20/12
to web2py-usuarios
Es que .compute se ejecuta desde que se hace la insersión,
actualización del registro según el manual, por lo que mi solución no
funciona. Hace falta algo antes de insertar que compruebe la tabla o
devuelva un error.

No sé cómo funciona, pero tenés la opción de llamar al proceso de
validación antes de hacer la modificación en la base de datos con
estos métodos. Puede ser que combinado con IS_NOT_IN_DB se comporte
como estás buscando, pero habría que ver si compute se ejecuta antes
de validar o no para este caso:

tabla.validate_and_insert(campo=valor, ...)
tabla.validate_and_update(campo=valor, ...)

Otra forma un poco más engorrosa sería tener en el model una función
especial para validar la combinación antes de hacer insert o update.

On 20 feb, 02:55, nicolás rosbaco <antiya...@gmail.com> wrote:
> acabo de probar, lo deje así:
>
> db.define_table('equipos',
>     Field('nombre', requires = [IS_NOT_EMPTY()], label = 'Nombre del
> equipo'),
>     Field('propietario',   'reference auth_user', required=True),
>     Field('edicion', 'reference ediciones', required=True),
>     Field('nombreclave', unique=True, compute=lambda r:
> str(r['edicion'])+r['nombre'],
> requires=IS_NOT_IN_DB(db,'equipos.nombreclave', error_message='prueba
> repetida')))
>
> a pesar de esto me permite insertar dos campos iguales:
> equipos.id<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...>
> equipos.nombre<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...>
> equipos.propietario<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...>
> equipos.edicion<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...>
> equipos.nombreclave<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...>
> 10 <http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/equipos/10>
> 1Tiburones11<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/equipos/11>
> Tiburones13<http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/auth_use...>
> El 20 de febrero de 2012 02:52, nicolás rosbaco <antiya...@gmail.com>escribió:
>
>
>
>
>
>
>
>
>
> > ahora lo pruebo... pero primero te pregunto:
> > esto es lo que creo:
> > si uso unique=True esto opera a nivel de la base de datos y no de los
> > validadores (javascript); en cambio si uso requires = IS_NO_IN_DB.....
> > estaría programando los validadores de js.... ¿es correcto?
>
> > aclaro más aún: debería hacer ambas cosas... creo yo. Uno es el modelo de
> > datos (la arquitectura de mi DB) y la segunda mi esquema de validadores
> > JS....
>
> > espero y corrección en esto
>
> > gracias
>
> > El 20 de febrero de 2012 01:04, Marco Mansilla <thebigsho...@gmail.com>escribió:
>
> > Solo por curiosidad... probaste usar IS_NOT_IN_DB(db,
> >> db.equipos.nombreclave) en lugar de unique?...
>
> >> > hola, y desde ya gracias por la atención, te comento:
>
> >> > intenté lo que me sugerís. tengo esto:
>
> >> > db.define_table('equipos',
> >> >     Field('nombre', requires = [IS_NOT_EMPTY()], label = 'Nombre del
> >> > equipo'),
> >> >     Field('propietario',   'reference auth_user', required=True),
> >> >     Field('edicion', 'reference ediciones', required=True),
> >> >     Field('nombreclave', unique=True, compute=lambda r:
> >> > str(r['edicion'])+r['nombre']))
>
> >> > resulta que puedo insertar sin problemas dos registros con la
> >> > "combinación prohibida"
>
> >> > equipos.id<
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...
>
> >> > equipos.nombre<
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...
>
> >> > equipos.propietario<
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...
>
> >> > equipos.edicion<
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...
>
> >> > equipos.nombreclave<
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/select/db?orderby=...
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/auth_use...
>
> >> > 1
> >> > <
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/ediciones/1>
> >> > 1Tiburones8<
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/equipos/8>
> >> > Tiburones1<
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/auth_user/1>
> >> > 1
> >> > <
> >>http://127.0.0.1:8000/desafio_curru_leuvu/appadmin/update/db/ediciones/1>
> >> > 1Tiburones
>
> >> > te paso acá la captura desde el appadmin de web2py, como podés ver
> >> > nombre clave se repite (estoy mas perdido que turco en la neblina)
>
> >> > bueno seguimos la charla... gracias
>
> >> > PD: estoy usando sqlite
>
> >> > El 19 de febrero de 2012 09:55, Alan Etkin <spame...@gmail.com>

Alan Etkin

unread,
Feb 20, 2012, 10:34:10 AM2/20/12
to web2py-usuarios
La única solución que encuentro para prevenir la modificación de la
base de datos es esta:

Creás un validador personalizado para uno de los campos clave. El
validador debe tomar el registro, hacer la concatenación con los
campos y hacer una consulta a la base de datos para comprobar que no
haya registros con igual combinación. Si pasa el requerimiento, el
validador devuelve la cadena combinada, si no, un mensaje de error.

Me parece que a DAL le falta algo que permita hacer operaciones con
campos antes de validar, no sé si hay algo que se pueda utilizar
directamente.

Los validadores personalizados están detallados en el manual en línea
http://www.latinuxpress.com/books/drafts/web2py/caps/cap7.html#validadores-personalizados

nicolás rosbaco

unread,
Feb 20, 2012, 10:47:10 AM2/20/12
to web2py-...@googlegroups.com
Hola te cuento: intenté hace un tiempo armar un validador personalizado por otro tema... pero con la info del manual no he podido... no termino de darme cuenta bien como es: donde va cada archivo, como se lo invocaría, etc.... si me podes pasar algo armado para seguir como ejemplo te lo agradeceré.

gracias desde ya

Marco Mansilla

unread,
Feb 20, 2012, 11:03:20 AM2/20/12
to web2py-...@googlegroups.com
Algunas cuestiones sobre esto:

-Lo mas adecuado (aprovechando la flexibilidad del framework), seria
buscar un algoritmo capaz de generar claves combinadas y que a su vez
haga la verificacion correspondiente en la DB, de manera que si existe
una clave igual en lugar de devolver un mensaje de error prueba una
combinacion nueva hasta encontrar una que sea unica.

-Si bien este campo puede ser una clave primaria en potencia, seria
mala practica aplicarlo como tan, las claves primarias deben ser
enteras y consecutivas, por eso es que ese campo DAL lo gestiona de
manera automatica, de hecho es mala practica dejar que los usuarios
puedan manipular una clave primaria.

-Usando los parametros de validacion en la declaracion de campos que
provee DAL se puede obtener el comportamiento de cualquier campo como
una clave primaria sin que este sea tal y manteniendo el campo ID
intacto.

-Si bien los validadores son importantes, seria bueno volver al primer
punto, algoritmos que ahorren tiempo... algo que se me ocurre, puede
que suene tonto.. es: "tomar tres campos, establecer un limite de 10
caracteres para la clave generada y con un random del contenido de los
tres campos ir concanteando los caracteres hasta llegar a 10, en lo
posible que no se repitan..."

PS: por ahi a Nico esto lo confunde un poco, pero admiro mucho su
perseverancia y las ganas que le pone a lo que esta haciendo.

Alan Etkin

unread,
Feb 20, 2012, 11:04:11 AM2/20/12
to web2py-usuarios
Estoy revisando el tema validadores y me equivoqué, no podés recuperar
un registro con una clase validador como se hace por ejemplo con
compute, porque lo que se valida es exclusivamente el valor del
formulario con los parámetros que le pases al constructor.

De todos modos, el método que te propuse al principio lo probé y
funciona en el sentido de que si se hace un insert con valores ya
existentes, la base de datos tira un error de integridad y los cambios
no se aplican (utilicé sqlite). El problema es que el error no es
atrapado por el controlador porque se genera fuera del proceso de
validación del formulario con compute, una vez que se aplicaron los
cambios, aparentemente.

Por eso me parece que falta algo en DAL que permita hacer
modificaciones a los datos de entrada previos a la validación en sí.
Esto se puede hacer de todos modos filtrando los valores del
formulario y utilizando la opción dbio=False en form.accepts(). La
dificultad de esto es que te obliga en cierto modo a agregar código en
el controlador en lugar de predefinir el comportamiento en el modelo

On 20 feb, 12:47, nicolás rosbaco <antiya...@gmail.com> wrote:
> Hola te cuento: intenté hace un tiempo armar un validador personalizado por
> otro tema... pero con la info del manual no he podido... no termino de
> darme cuenta bien como es: donde va cada archivo, como se lo invocaría,
> etc.... si me podes pasar algo armado para seguir como ejemplo te lo
> agradeceré.
>
> gracias desde ya
>
> El 20 de febrero de 2012 12:34, Alan Etkin <spame...@gmail.com> escribió:
>
>
>
>
>
>
>
>
>
> > La única solución que encuentro para prevenir la modificación de la
> > base de datos es esta:
>
> > Creás un validador personalizado para uno de los campos clave. El
> > validador debe tomar el registro, hacer la concatenación con los
> > campos y hacer una consulta a la base de datos para comprobar que no
> > haya registros con igual combinación. Si pasa el requerimiento, el
> > validador devuelve la cadena combinada, si no, un mensaje de error.
>
> > Me parece que a DAL le falta algo que permita hacer operaciones con
> > campos antes de validar, no sé si hay algo que se pueda utilizar
> > directamente.
>
> > Los validadores personalizados están detallados en el manual en línea
>
> >http://www.latinuxpress.com/books/drafts/web2py/caps/cap7.html#valida...

nicolás rosbaco

unread,
Feb 20, 2012, 11:28:04 AM2/20/12
to web2py-...@googlegroups.com
Hola marco muchas gracias por la sugerencia... voy por parte (como Jack):

El 20 de febrero de 2012 13:03, Marco Mansilla <thebig...@gmail.com> escribió:
Algunas cuestiones sobre esto:

-Lo mas adecuado (aprovechando la flexibilidad del framework), seria
buscar un algoritmo capaz de generar claves combinadas y que a su vez
haga la verificacion correspondiente en la DB, de manera que si existe
una clave igual en lugar de devolver un mensaje de error prueba una
combinacion nueva hasta encontrar una que sea unica.

Me gusta

-Si bien este campo puede ser una clave primaria en potencia, seria
mala practica aplicarlo como tan, las claves primarias deben ser
enteras y consecutivas, por eso es que ese campo DAL lo gestiona de
manera automatica, de hecho es mala practica dejar que los usuarios
puedan manipular una clave primaria.

Ok. lo tome como PK porque en mysql de ese modo podía definir subconjuntos de campos cuya combinación sea única...
 
Soy de la idea que no esta mal eso de usar subconjuntos de campos como PK... estaría bueno pensarlo para modificar el DAL... pero bueno es materia de discusión, y definitivamente: lejos estoy de ser un "experto" ahí...  así que una efímera sugerencia


-Usando los parametros de validacion en la declaracion de campos que
provee DAL se puede obtener el comportamiento de cualquier campo como
una clave primaria sin que este sea tal y manteniendo el campo ID
intacto.

si eso sí, pero se los toma individualmente, no como conjunto de campos... hace único el año de edición y hace único el nombre del equipo... lo que no me permite 2011->tiburones, 2012->tiburones (eso debería estar permitido, no por el contrario: 2012->tiburones, 2012->tiburones.... esa es la ocurrencia prohibida)

-Si bien los validadores son importantes, seria bueno volver al primer
punto, algoritmos que ahorren tiempo... algo que se me ocurre, puede
que suene tonto.. es: "tomar tres campos, establecer un limite de 10
caracteres para la clave generada y con un random del contenido de los
tres campos ir concanteando los caracteres hasta llegar a 10, en lo
posible que no se repitan..."

Ok. ahí entiendo bien la idea, lo voy a ver por ese lado.... pero claro requiere alguna liñita más de código ;-)


De todos modos ando buscando hace algún tiempo un ejemplo concreto de un validador personalizado... me harán falta en algún momento (lo que vi en la documentación no me esclarece casi nada)
 

Marco mil gracias che!

nicolás rosbaco

unread,
Feb 20, 2012, 12:14:21 PM2/20/12
to web2py-...@googlegroups.com
Hola Alan. gracias

El 20 de febrero de 2012 13:04, Alan Etkin <spam...@gmail.com> escribió:
Estoy revisando el tema validadores y me equivoqué, no podés recuperar
un registro con una clase validador como se hace por ejemplo con
compute, porque lo que se valida es exclusivamente el valor del
formulario con los parámetros que le pases al constructor.

De todos modos, el método que te propuse al principio lo probé y

me perdí con tanto mail.... cuál es?

usar el campo computado con unicidad a mi no me anduvo... ¿es ese?

gracias

Alan Etkin

unread,
Feb 20, 2012, 2:56:13 PM2/20/12
to web2py-usuarios
Si pero la prueba la hice con sqlite (si intentás pasar un campo
combinado duplicado, devuelve IntegrityError)

Acá te paso un ejemplo:

db.define_table("combinada", Field("combinado", compute=lambda row:
row.primero+row.segundo, unique=True, writable=False),
Field("primero", requires=IS_NOT_EMPTY()), Field("segundo",
requires=IS_NOT_EMPTY()))

db.combinada.combinado.requires =
[IS_NOT_IN_DB(db(db.combinada.combinado==str(request.vars.primero)
+str(request.vars.segundo)), "combinada.combinado"), IS_NOT_EMPTY()]

Alan Etkin

unread,
Feb 20, 2012, 3:59:38 PM2/20/12
to web2py-usuarios
Hice un post en la lista en inglés y me pasaron esta receta:

db.define_table("combinada", Field("combinado", compute=lambda row:
row.primero+row.segundo, requires=IS_NOT_EMPTY(), unique=True,
writable=False), Field("primero"), Field("segundo",
requires=IS_NOT_EMPTY()))

db.combinada.primero.requires =
[IS_NOT_IN_DB(db(db.combinada.segundo==request.vars.segundo),
"combinada.primero", error_message='The combination of primero and
segundo must be unique'), IS_NOT_EMPTY()]

Con este código, IS_NOT_IN_DB comprueba que no existan registros
duplicados para .primero y .segundo) al validar el formulario. Incluso
tené en cuenta que no es indispensable el campo combinado, porque lo
podés generar en cada consulta como un campo virtual.


On 20 feb, 14:14, nicolás rosbaco <antiya...@gmail.com> wrote:
> Hola Alan. gracias
>

puercoespin

unread,
Feb 21, 2012, 2:21:53 PM2/21/12
to web2py-usuarios
Creo que incluso puede ser un poco más fácil sin campos virtuales ni
compute:

db.define_table('tabla1',Field('valor1',),Field('valor2',))

db.tabla1.valor2.requires=IS_NOT_IN_DB(db(db.tabla1.valor1==request.vars.valor1),
db.tabla1.valor2)

Esto debería funcionar...

Saludos

nicolás rosbaco

unread,
Feb 21, 2012, 3:00:44 PM2/21/12
to web2py-...@googlegroups.com
mmm que raro.... lo voy a probar luego.

gracias

Jose

unread,
Feb 21, 2012, 6:10:46 PM2/21/12
to web2py-...@googlegroups.com
"si uso unique=True esto opera a nivel de la base de datos y no de los validadores (javascript); en cambio si uso requires = IS_NO_IN_DB..... estaría programando los validadores de js.... ¿es correcto?"

Solo una aclaración, las validaciones se ejecutan en el servidor y no en el cliente con javascript.

Saludos
Jose

nicolás rosbaco

unread,
Feb 21, 2012, 6:38:18 PM2/21/12
to web2py-...@googlegroups.com
bien esa aclaración.... gracias
Reply all
Reply to author
Forward
0 new messages