Keeping database foreign keys consistent











up vote
3
down vote

favorite












I am trying to write a django app with a PostgreSQL database. My problem can be reduced to the following minimal example:



Organization




  • has 0 ... N workers


Worker




  • belongs to an organization


Task




  • belongs to an organization

  • is assigned to no-one, or assigned to one worker within the same organization


I am unsure of how to design the database in a way that prevents inconsistent state - since both Worker and Task need to have a foreign key pointing to the Organization they belong to, it is possible to end up with an inconsistent state where a worker from organization A is assigned a task from organization B.



The only solution I could think of was verifying the constraint in the save method:



class Organization(models.Model):
name = models.CharField(max_length=255)


class Worker(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)


class Task(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
assigned_to = models.ForeignKey(Worker, null=True, on_delete=models.SET_NULL)

def save(self, *args, **kwargs):
if self.assigned_to and self.assigned_to.organization != self.organization:
raise ValidationError('Task and Worker belong in different Organizations.')
return super(Task, self).save(*args, **kwargs


Is it possible to create a better solution which still prevents inconsistent state? My own solution doesn't seem like a good one from a database design point of view.










share|improve this question







New contributor




けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • It would be great if the downvoter taught me what I did wrong, instead of just voting me down...
    – けんじ
    yesterday










  • One factor to consider is what happens if a worker changes organizations. Is that allowed? Do their tasks switch organizations too, or do they become unassigned? And so on. That might inform your design.
    – Kevin Christopher Henry
    yesterday






  • 1




    You want a task FK (worker, org) to worker. A FK says subrows must appear elsewhere a PKs. We need to identify all the restrictions that our database tables have & declare what we can as constraints & enforce others via triggers. This is a faq, that you might find if you googled many clear concise specific phrasings of your question/problem/goal. with & without your particular strings/names. PS Re downvoter presumably they think it is either unclear and/or not useful (maybe because it's a faq or there's no Minimal, Complete, and Verifiable example)--see the downvote arrow mouseover text.
    – philipxy
    yesterday












  • You'd need to implement a check. I got it from this answer: stackoverflow.com/a/39884447/1637351
    – schillingt
    23 hours ago










  • To add to @philipxy answer, I have found this post which also suggests a composite foreign key. To that end I have found this django module: django-composite-foreignkey
    – CoffeeBasedLifeform
    6 hours ago















up vote
3
down vote

favorite












I am trying to write a django app with a PostgreSQL database. My problem can be reduced to the following minimal example:



Organization




  • has 0 ... N workers


Worker




  • belongs to an organization


Task




  • belongs to an organization

  • is assigned to no-one, or assigned to one worker within the same organization


I am unsure of how to design the database in a way that prevents inconsistent state - since both Worker and Task need to have a foreign key pointing to the Organization they belong to, it is possible to end up with an inconsistent state where a worker from organization A is assigned a task from organization B.



The only solution I could think of was verifying the constraint in the save method:



class Organization(models.Model):
name = models.CharField(max_length=255)


class Worker(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)


class Task(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
assigned_to = models.ForeignKey(Worker, null=True, on_delete=models.SET_NULL)

def save(self, *args, **kwargs):
if self.assigned_to and self.assigned_to.organization != self.organization:
raise ValidationError('Task and Worker belong in different Organizations.')
return super(Task, self).save(*args, **kwargs


Is it possible to create a better solution which still prevents inconsistent state? My own solution doesn't seem like a good one from a database design point of view.










share|improve this question







New contributor




けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • It would be great if the downvoter taught me what I did wrong, instead of just voting me down...
    – けんじ
    yesterday










  • One factor to consider is what happens if a worker changes organizations. Is that allowed? Do their tasks switch organizations too, or do they become unassigned? And so on. That might inform your design.
    – Kevin Christopher Henry
    yesterday






  • 1




    You want a task FK (worker, org) to worker. A FK says subrows must appear elsewhere a PKs. We need to identify all the restrictions that our database tables have & declare what we can as constraints & enforce others via triggers. This is a faq, that you might find if you googled many clear concise specific phrasings of your question/problem/goal. with & without your particular strings/names. PS Re downvoter presumably they think it is either unclear and/or not useful (maybe because it's a faq or there's no Minimal, Complete, and Verifiable example)--see the downvote arrow mouseover text.
    – philipxy
    yesterday












  • You'd need to implement a check. I got it from this answer: stackoverflow.com/a/39884447/1637351
    – schillingt
    23 hours ago










  • To add to @philipxy answer, I have found this post which also suggests a composite foreign key. To that end I have found this django module: django-composite-foreignkey
    – CoffeeBasedLifeform
    6 hours ago













up vote
3
down vote

favorite









up vote
3
down vote

favorite











I am trying to write a django app with a PostgreSQL database. My problem can be reduced to the following minimal example:



Organization




  • has 0 ... N workers


Worker




  • belongs to an organization


Task




  • belongs to an organization

  • is assigned to no-one, or assigned to one worker within the same organization


I am unsure of how to design the database in a way that prevents inconsistent state - since both Worker and Task need to have a foreign key pointing to the Organization they belong to, it is possible to end up with an inconsistent state where a worker from organization A is assigned a task from organization B.



The only solution I could think of was verifying the constraint in the save method:



class Organization(models.Model):
name = models.CharField(max_length=255)


class Worker(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)


class Task(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
assigned_to = models.ForeignKey(Worker, null=True, on_delete=models.SET_NULL)

def save(self, *args, **kwargs):
if self.assigned_to and self.assigned_to.organization != self.organization:
raise ValidationError('Task and Worker belong in different Organizations.')
return super(Task, self).save(*args, **kwargs


Is it possible to create a better solution which still prevents inconsistent state? My own solution doesn't seem like a good one from a database design point of view.










share|improve this question







New contributor




けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I am trying to write a django app with a PostgreSQL database. My problem can be reduced to the following minimal example:



Organization




  • has 0 ... N workers


Worker




  • belongs to an organization


Task




  • belongs to an organization

  • is assigned to no-one, or assigned to one worker within the same organization


I am unsure of how to design the database in a way that prevents inconsistent state - since both Worker and Task need to have a foreign key pointing to the Organization they belong to, it is possible to end up with an inconsistent state where a worker from organization A is assigned a task from organization B.



The only solution I could think of was verifying the constraint in the save method:



class Organization(models.Model):
name = models.CharField(max_length=255)


class Worker(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)


class Task(models.Model):
name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
assigned_to = models.ForeignKey(Worker, null=True, on_delete=models.SET_NULL)

def save(self, *args, **kwargs):
if self.assigned_to and self.assigned_to.organization != self.organization:
raise ValidationError('Task and Worker belong in different Organizations.')
return super(Task, self).save(*args, **kwargs


Is it possible to create a better solution which still prevents inconsistent state? My own solution doesn't seem like a good one from a database design point of view.







python django database schema normalization






share|improve this question







New contributor




けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question







New contributor




けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question






New contributor




けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked yesterday









けんじ

191




191




New contributor




けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






けんじ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












  • It would be great if the downvoter taught me what I did wrong, instead of just voting me down...
    – けんじ
    yesterday










  • One factor to consider is what happens if a worker changes organizations. Is that allowed? Do their tasks switch organizations too, or do they become unassigned? And so on. That might inform your design.
    – Kevin Christopher Henry
    yesterday






  • 1




    You want a task FK (worker, org) to worker. A FK says subrows must appear elsewhere a PKs. We need to identify all the restrictions that our database tables have & declare what we can as constraints & enforce others via triggers. This is a faq, that you might find if you googled many clear concise specific phrasings of your question/problem/goal. with & without your particular strings/names. PS Re downvoter presumably they think it is either unclear and/or not useful (maybe because it's a faq or there's no Minimal, Complete, and Verifiable example)--see the downvote arrow mouseover text.
    – philipxy
    yesterday












  • You'd need to implement a check. I got it from this answer: stackoverflow.com/a/39884447/1637351
    – schillingt
    23 hours ago










  • To add to @philipxy answer, I have found this post which also suggests a composite foreign key. To that end I have found this django module: django-composite-foreignkey
    – CoffeeBasedLifeform
    6 hours ago


















  • It would be great if the downvoter taught me what I did wrong, instead of just voting me down...
    – けんじ
    yesterday










  • One factor to consider is what happens if a worker changes organizations. Is that allowed? Do their tasks switch organizations too, or do they become unassigned? And so on. That might inform your design.
    – Kevin Christopher Henry
    yesterday






  • 1




    You want a task FK (worker, org) to worker. A FK says subrows must appear elsewhere a PKs. We need to identify all the restrictions that our database tables have & declare what we can as constraints & enforce others via triggers. This is a faq, that you might find if you googled many clear concise specific phrasings of your question/problem/goal. with & without your particular strings/names. PS Re downvoter presumably they think it is either unclear and/or not useful (maybe because it's a faq or there's no Minimal, Complete, and Verifiable example)--see the downvote arrow mouseover text.
    – philipxy
    yesterday












  • You'd need to implement a check. I got it from this answer: stackoverflow.com/a/39884447/1637351
    – schillingt
    23 hours ago










  • To add to @philipxy answer, I have found this post which also suggests a composite foreign key. To that end I have found this django module: django-composite-foreignkey
    – CoffeeBasedLifeform
    6 hours ago
















It would be great if the downvoter taught me what I did wrong, instead of just voting me down...
– けんじ
yesterday




It would be great if the downvoter taught me what I did wrong, instead of just voting me down...
– けんじ
yesterday












One factor to consider is what happens if a worker changes organizations. Is that allowed? Do their tasks switch organizations too, or do they become unassigned? And so on. That might inform your design.
– Kevin Christopher Henry
yesterday




One factor to consider is what happens if a worker changes organizations. Is that allowed? Do their tasks switch organizations too, or do they become unassigned? And so on. That might inform your design.
– Kevin Christopher Henry
yesterday




1




1




You want a task FK (worker, org) to worker. A FK says subrows must appear elsewhere a PKs. We need to identify all the restrictions that our database tables have & declare what we can as constraints & enforce others via triggers. This is a faq, that you might find if you googled many clear concise specific phrasings of your question/problem/goal. with & without your particular strings/names. PS Re downvoter presumably they think it is either unclear and/or not useful (maybe because it's a faq or there's no Minimal, Complete, and Verifiable example)--see the downvote arrow mouseover text.
– philipxy
yesterday






You want a task FK (worker, org) to worker. A FK says subrows must appear elsewhere a PKs. We need to identify all the restrictions that our database tables have & declare what we can as constraints & enforce others via triggers. This is a faq, that you might find if you googled many clear concise specific phrasings of your question/problem/goal. with & without your particular strings/names. PS Re downvoter presumably they think it is either unclear and/or not useful (maybe because it's a faq or there's no Minimal, Complete, and Verifiable example)--see the downvote arrow mouseover text.
– philipxy
yesterday














You'd need to implement a check. I got it from this answer: stackoverflow.com/a/39884447/1637351
– schillingt
23 hours ago




You'd need to implement a check. I got it from this answer: stackoverflow.com/a/39884447/1637351
– schillingt
23 hours ago












To add to @philipxy answer, I have found this post which also suggests a composite foreign key. To that end I have found this django module: django-composite-foreignkey
– CoffeeBasedLifeform
6 hours ago




To add to @philipxy answer, I have found this post which also suggests a composite foreign key. To that end I have found this django module: django-composite-foreignkey
– CoffeeBasedLifeform
6 hours ago

















active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});






けんじ is a new contributor. Be nice, and check out our Code of Conduct.










 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53179127%2fkeeping-database-foreign-keys-consistent%23new-answer', 'question_page');
}
);

Post as a guest





































active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes








けんじ is a new contributor. Be nice, and check out our Code of Conduct.










 

draft saved


draft discarded


















けんじ is a new contributor. Be nice, and check out our Code of Conduct.













けんじ is a new contributor. Be nice, and check out our Code of Conduct.












けんじ is a new contributor. Be nice, and check out our Code of Conduct.















 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53179127%2fkeeping-database-foreign-keys-consistent%23new-answer', 'question_page');
}
);

Post as a guest




















































































Popular posts from this blog

横浜市

Rostock

Europa