2015-04-24 18 views
6

Document adlı bir modelim var ve Document numaralı yabancı bir anahtarla yeni bir masa eklemek, bunun üstüne oturan DocumentCluster eklemek istiyorum. Güneye kullanarak bu tabloyu eklediğinizdeBirincil anahtar nasıl ayarlanır, sonra otomatik alanlara dönüştürülür?

class DocumentCluster(models.Model): 
    sub_document = models.ForeignKey(Document) 
    ...lots of fields here... 

, ben birincil anahtar ve aynı değere yabancı anahtarını ayarlayarak doldurmanız gerekiyor. Şu anda 12 kişilik bir pk ile Document nesne varsa o garip görünse

Örneğin, yeni DocumentCluster nesne 12 bir pk ve Document sayı 12.

için yabancı anahtar olacak bizim birlikte Önemli bir neden olduğu için yabancı anahtar değerleriyle eşleşecek DocumentClusterpk değerlerine ihtiyacınız var. URL'lerimizde Documentpk'u kullanıyoruz, ancak değişiklikten sonra URL'ler Document değil DocumentCluster yüklenecek, DocumentClusterpkDocument ile aynı olması gerekir.

Bu işlem bittiğinde, DocumentCluster numaralı PK'nin bir AutoField olmasını ve taşınan en yüksek değerden artırılmasını istiyorum.

Bu yapılabilir mi?

+1

pk'yi neden ayarlamanız gerekiyor? Doğal bir anahtar olarak içerikle ilgisi olmamalıdır. İlişkisini belirlemek için fk var. –

+0

PK'yi URL'lerimizde kullanıyoruz, ancak değişiklikten sonra, URL'ler bir 'DocumentCluster' değil' DocumentCluster 'yükleyecek, böylece PK'nin daha önce olduğu gibi olması gerekecek. – mlissner

cevap

1

SenDocument-DocumentCluster.pkbir FK sahip ve aynı zamanda DocumentCluster.pk bir serial sütun yapamaz. Bu bir çelişki. İki ayrı sütun olmalı. Sana tersi anlamına varsayalım: DocumentCluster-Documentdan. Bu SQL DDL ile yapılabilir

komutları:

BEGIN; 

CREATE TABLE DocumentCluster (pk serial, lots text, of_fields int, ...); 

INSERT INTO DocumentCluster (pk, lots, of_fields, ...) 
SELECT pk, lots, of_fields, ... FROM Document 
ORDER BY pk; -- optional 

ALTER TABLE DocumentCluster ADD CONSTRAINT DocumentCluster_pkey 
PRIMARY KEY (pk); -- could be integrated in CREATE TABLE 

ALTER TABLE Document -- and not the other way round! 
ADD CONSTRAINT pk_fk FOREIGN KEY (pk) REFERENCES DocumentCluster(pk); 

SELECT setval(pg_get_serial_sequence('DocumentCluster', 'pk'), max(pk)) 
FROM DocumentCluster; -- update SEQUENCE to highest value 

COMMIT; 
  • aynı şeyi olurdu bu durumda Kişisel gizleme tabakası (Django) "Document" gibi çift tırnaklı isimleri kullanıyor olabilir, ...

  • About setval().

  • About pg_get_serial_sequence()

  • PK, CREATE TABLE bildirimi içinde bildirilebilir, ancak satırlar eklendikten sonra eklemek daha ucuzdur; bu nedenle pk, unique not null şeklindedir.

fazla açıklama ve bağlantılar:

+0

SQL'inizin doğru olduğunu düşünüyorum, ancak herhangi bir neden de, herhangi bir seri pk ve DocumentCluster modelindeki sub_document yabancı anahtarına sahip olamaz. Ve 'Obfuscation layer' ile ne kastettiğinizden emin değilim. Sub_document alanı/sütununa eklediyseniz, SQL daha net olur. – nmgeek

+0

@nmgeek: Yazdığım gibi: İki ayrı sütun olmalı - sizin de önerdiğiniz gibi. Teoride referanslama sütunu da bir "seri" olabilir. Ancak, bir sıradaki değerleri çizmek zor olmakla birlikte, başka bir tabloda anahtarın bir referansı olması gerekir. Biraz açıklığa kavuştum. 'Sub_document' alanını eklemedim çünkü FK'nin yönünü tersine çevirdikten sonra gerekli olduğunu düşünmüyorum. –

+0

Asıl soruda dediğim gibi, bunu Güney ile mümkün olduğu kadar çok yapmayı umuyordum, böylece getirdiği tüm avantajlara sahip olabiliyorum.Verileri Güney kullanarak taşıyabileceğim bir yol varsa, pk'yi seri olarak güncellemelisiniz, bu benim tercih ettiğim çözüm olabilir. – mlissner

1

kullanma güney Bana bunun olacağını düşündüm çok daha basittir. DocumentCluster modelini ekleyen şema geçişini oluşturmak için her zamanki gibi güney şemamigration komutunu kullanın.

def forwards(self, orm): 
    max_id = -1 
    clusters_added = 0                           
    for document in orm.Document.objects.all(): 
     cluster = orm.DocumentCluster() 
     cluster.id = document.id 
     cluster.sub_document = document 
     cluster.save() 
     max_id = max(max_id, document.id) 
     clusters_added +=1 
    if max_id >= clusters_added: 
     orm.DocumentCluster.objects.raw("SELECT "\ 
      "setval(pg_get_serial_sequence('yourapp_documentcluster',"\ 
             "'id'), %d)" % max_id+1) 
: benzediğini böylece

./manage.py datamigration yourapp populate_clusters 

Sonra çıkan piton geçiş dosyasındaki ileri yönteminde doldurun:

Ardından bir sonraki geçiş için bir iskelet oluşturmak için datamigration komutunu kullanın

(Bu veri taşımada ters yöntemi, tüm DocumentModel örneklerini silecektir.)

Bu 'populate_clusters' geçiş adımı, DocumentCluster tablosunu ekleyen geçiş adımından hemen sonra çalıştırılırsa, DocumentCluster tablosu boştur ve seri/autokey sayacı sıfırdan başlar. Herhangi bir Döküman'u hiçbir zaman silmediyseniz, seri/autokey sayacı değeri Belge pk değerleri için bir sonraki kullanılmayan değerde sona erer ve Erwin'in cevabında gösterildiği gibi vurmak zorunda kalmazsınız. ancak, Belge örneklerini sildiniz ya da bir şekilde bir id değeri atlandı ise o zaman seri/autokey sayaç SQL kullanarak o yükseltmek gerekir, varsa

. Django, ham SQL'i doğrudan yürütmek için bir yol sağlar. (Ham SQL kullanmadan yapabileceğinizi düşünmüyorum).

Güvenlik açısından, cluster.id değeri < = döngüde görülen maksimum document.id değeri olup olmadığını kontrol etmelisiniz.

+0

Amaçlarım için daha iyi bir yanıt gibi görünüyor. Şaşırdım Django, bir ana anahtarın değerini manüel olarak ayarlamanıza izin verecek, ama eğer bu kadar gerekiyorsa, o zaman iş yapıyoruz! – mlissner

+0

Başka bir şey üzerinde çalışırken, Django'nun aslında otomatik birincil anahtarların manuel olarak ayarlanmasıyla ilgili belgeleri olduğunu gördüm. https://docs.djangoproject.com/en/1.8/ref/models/instances/#explicitly-specifying-auto-primary-key-values – mlissner

İlgili konular