2010-04-20 9 views
21

Attualmente sto costruendo un progetto che implica molta intelligenza collettiva. Ogni utente che visita il sito Web crea un profilo univoco e i suoi dati vengono successivamente utilizzati per calcolare le corrispondenze migliori per se stessi e gli altri utenti.Campo di incremento automatico di Django BigInteger come chiave primaria?

Per impostazione predefinita, Django crea un campo INT (11) id per gestire i modelli chiavi primarie. Sono preoccupato per il fatto che questo sia stato sorvolato molto rapidamente (ad esempio, i dispositivi 2.4b che visitano la pagina senza l'impostazione preliminare dei cookie). Come posso cambiarlo per essere rappresentato come BIGINT in MySQL e long() all'interno di Django stesso?

ho trovato che potevo fare quanto segue (http://docs.djangoproject.com/en/dev/ref/models/fields/#bigintegerfield):

class MyProfile(models.Model): 
    id = BigIntegerField(primary_key=True) 

Ma c'è un modo per rendere AutoIncrement, come al solito id campi? Inoltre, posso renderlo non firmato in modo da ottenere più spazio da riempire?

Grazie!

risposta

6

In seguito è possibile modificare la tabella. Potrebbe essere una soluzione migliore.

1

Ho anche avuto lo stesso problema. Sembra che non ci sia supporto per i campi auto BigInteger in django.

Ho provato a creare un campo personalizzato BigIntegerAutoField ma ho riscontrato un problema con il sistema di migrazione sud (south non è riuscito a creare la sequenza per il mio campo).

Dopo aver dato un paio prova di differenti approcci ho deciso di seguire il consiglio di Matteo e fare alter table (ad esempio ALTER TABLE table_name ALTER COLUMN id TYPE bigint; in postgre)

sarebbe bello avere una soluzione supportata da Django (come costruito in BigIntegerAutoField) e sud.

+0

credo che questo non avrebbe aiutato, come Django sarebbe ancora messo id in int normale, che è ancora troppo piccolo. – letoosh

+0

Niente affatto, il punto qui è come l'ID è memorizzato sul database. (Lungo) Il tipo intero in python ha una precisione 'illimitata'. Puoi anche verificare come BigIntegerField è impiantato in Django 1.2 - eredita direttamente IntegreField, senza modificare il tipo interno per memorizzare il valore (che è int in questo caso) – dzida

+0

Hai ragione, mio ​​male ... – letoosh

13

NOTA: questa risposta è stata modificata in base al codice di Larry. precedente soluzione estesa fields.BigIntegerField, ma meglio di estendere fields.AutoField

Ho avuto lo stesso problema e risolto con la seguente codice:

from django.db.models import fields 
from south.modelsinspector import add_introspection_rules 

class BigAutoField(fields.AutoField): 
    def db_type(self, connection): 
     if 'mysql' in connection.__class__.__module__: 
      return 'bigint AUTO_INCREMENT' 
     return super(BigAutoField, self).db_type(connection) 

add_introspection_rules([], ["^MYAPP\.fields\.BigAutoField"]) 

A quanto pare questo sta lavorando bene con le migrazioni sud.

+2

Ho down-votato questa risposta perché ho usato questa soluzione in produzione e ha causato un bug di produzione. Il problema è che poiché questo campo non estende AutoField, Django non recupera l'ID dal DB dopo aver scritto un nuovo modello. Questa è una differenza di comportamento rispetto ai tipici campi di incremento automatico dell'ID. Proporrò una soluzione diversa di seguito. Sto prendendo in prestito il 99,9% di questa soluzione, ma non voglio che gli altri facciano lo stesso errore. – Larry

+0

Tks! Vorrei poter ricordare dove ho bisogno di questo prima :-). – lfagundes

+2

Cosa succede se si utilizza PostgreSQL? Forse potremmo aggiungere un'altra condizione in questa risposta: 'elif 'postgres' in connection .__ class __.__ module__: return 'bigserial'' – Pawamoy

3

Come indicato in precedenza, è possibile modificare la tabella in seguito. Questa è una buona soluzione.

Per fare ciò senza dimenticare, è possibile creare un modulo di gestione sotto il pacchetto dell'applicazione e utilizzare il segnale post_syncdb.

https://docs.djangoproject.com/en/dev/ref/signals/#post-syncdb

Ciò può causare a filo django-admin.py al sicuro. Ma è ancora la migliore alternativa che conosca.

16

Ispirato lfagundes ma con una piccola ma importante correzione:

class BigAutoField(fields.AutoField): 
    def db_type(self, connection): # pylint: disable=W0621 
     if 'mysql' in connection.__class__.__module__: 
      return 'bigint AUTO_INCREMENT' 
     return super(BigAutoField, self).db_type(connection) 

add_introspection_rules([], [r"^a\.b\.c\.BigAutoField"]) 

Avviso invece di estendere BigIntegerField, mi rivolgo AutoField. Questa è una distinzione importante. Con AutoField, Django recupererà l'ID AUTO INCREMENTED dal database, mentre BigInteger no.

Una preoccupazione quando si passa da BigIntegerField a AutoField è stata la trasmissione dei dati a un int in AutoField.

Avviso da AutoField di Django:

def to_python(self, value): 
    if value is None: 
     return value 
    try: 
     return int(value) 
    except (TypeError, ValueError): 
     msg = self.error_messages['invalid'] % str(value) 
     raise exceptions.ValidationError(msg) 

e

def get_prep_value(self, value): 
    if value is None: 
     return None 
    return int(value) 

Si scopre questo è OK, come verificato in un guscio di pitone:

>>> l2 = 99999999999999999999999999999 
>>> type(l2) 
<type 'long'> 
>>> int(l2) 
99999999999999999999999999999L 
>>> type(l2) 
<type 'long'> 
>>> type(int(l2)) 
<type 'long'> 

In altre parole, la fusione a un int non verrà troncato il numero, né cambierà il tipo sottostante.

+0

Ho scoperto che avevo anche bisogno di un BigForeignKey per fare anche i tipi fk sono impostati su bigint – shangxiao

+0

La domanda di "OK" relativa alle chiamate a int() nella dichiarazione della classe AutoField dipende dal sistema Se si utilizza un sistema a 64 bit, verrà restituito sys.maxint il giusto valore e tu sei ok Se per qualsiasi motivo nel 2015 sei su un sistema a 32 bit, beh, allora Bigint non funzionerebbe comunque in postgres :-) –