django multiple models with same foreign key - django

I have django app that is tracking contracts, locations, and products for various clients. Contracts, Locations each reference the client with a ForeignKey.
class Location(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
...
class Contract(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
...
I also have a product that will be deployed in a location, on a contract, for a client. I need to include client in product because the reference is used to secure the record to that client.
class Product(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
location = models.ForeignKey(Location, on_delete=models.CASCADE)
....
What is the best way to make sure a product is never created that contains a different client reference across all 3 models? I was thinking I could use a pre_save signal but I would rather use a database constraint but I don't see a database constraint to force this.

Did you look into the unique_together. I think this will do what you want to do.
Also keep the following in mind.
UniqueConstraint provides more functionality than unique_together. unique_together may be deprecated in the future.

Related

Editing dynamic choice fields in Django

I have a standard Django blog with a Post model, only on the model I have added a ManyToManyField for approvers, the idea being that the backend passes the post to 2 or more approvers to confirm the content before it is published.
class Post(models.Model):
author = models.ForeignKey(
get_user_model(), null=True, on_delete=models.SET_NULL)
title = models.CharField(max_length=30)
content = models.CharField(max_length=30)
approvers = models.ManyToManyField(Approvers)
I will probably learn towards something like django-fsm to create a finite state machine for the Post model to govern whether it is draft/in approval/published, but I would like to be able to change the approvers field so that the number and order of approvers (users) can be changed dynamically by the user.
What is the best way to do this? I thought I could try and change the approvers field to a JSONField so that users can add / delete / change the order of approvers and then handle the interpretation in the frontend and write some function to interface with django-fsm, but this feels like it conflates things too much. Am I missing a simpler route?
Why not make another model to do so like
class PostApprover(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='approvers')
user = models.ForeignKey(Approver, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
To access order in which post(let say with id 5) is approved (descending).you can do like
post = Post.objects.get(id=5)
post.approvers.order_by('-created_at')
you can change the value of created_at to change the order.
Or you can also make an integer field that determines your order

How to save Django model instances with circular dependency?

models.py:
class Server(models.Model):
name = models.CharField(max_length=100, unique=True)
last_interaction = models.OneToOneField('Interaction',
on_delete=models.CASCADE,
related_name='server')
class Interaction(models.Model):
client = models.CharField(max_length=100)
time = models.DateTimeField()
server = models.ForeignKey(Server,
on_delete=models.CASCADE,
related_name="interactions")
How do I save instances? (When I want to save one, the other isn't saved yet, so it can't be saved)
Notes:
I been there. The accepted answer doesn't provide a solution to the problem. Indeed at runtime there will be new servers and clients.
I know this schema makes little sense, but I still want to find a way to work with this.
I know the on_delete=cascade in Server is dangerous. I plan to fix it once I solve this problem.
Fundamentally, if you create a circular foreign key relationship, at least one of them must be nullable. For example:
class Interaction(models.Model):
client = models.CharField(max_length=100)
time = models.DateTimeField()
server = models.ForeignKey(Server,
on_delete=models.CASCADE,
related_name="interactions",
null=True
)
You'll be able to create Interaction objects without having the corresponding Server instance ready.

Using OneToOneField with multiple versions of data

I want to have two versions of the same model while still benefiting from OneToOneField's reverse relation.
For example, let's say that I have the following models:
class Company(models.Model):
exists = models.BooleanField()
class ExtraInforation(models.Model):
company = models.OneToOneField(Company)
wealthy = models.BooleanField()
At this point my code uses the brilliance of the OneToOneField reverse relation, doing company.extrainformationcalls all over the place.
Then I get a new requirement: we can't trust the ExtraInformation without verifying it first! Pfft, any company could claim that it's wealty...
Any changes to ExtraInformation need to be confirmed before publishing. Let's say that the company isn't wealthy when it registers and that information gets confirmed. Later the company wants to mark itself wealthy. At that point there needs to be the confirmed/public version of ExtraInformation and the unconfirmed version that needs to be confirmed.
I want to be able to still keep those handy OneToOneField reverse relation calls but also have another version of the same data. The problem is, of course, that there can be only one row with reference to this company in the OneToOneField.
Currently my solution is to create a new table:
class ExtraInforationUnconfirmed(models.Model):
company = models.OneToOneField(Company)
wealthy = models.BooleanField()
Once the information is confirmed, the fields are copied from ExtraInforationUnconfirmed to ExtraInformation. This solution isn't very DRY or clean.
What would be the best way to solve this issue?
I studied proxy models and model inheritance. The best alternative way I could think of is to have a base model and inherit two models that have both have OneToOneField relation of their own to Company.
Add a boolean feild to the model and change it to true when confirmed:
class ExtraInforation(models.Model):
company = models.OneToOneField(Company)
wealthy = models.BooleanField()
confirmed = models.BooleanField(default=False)
UPDATE
Based on your comment I suggest a version filed which can be a simple integer or a datetime. I would avoid creating two models at any cost :)
class ExtraInforation(models.Model):
company = models.ForeignKey(Company, related_name='extrainformations')
wealthy = models.BooleanField()
# version = models.PositiveIntegerField()
version = models.DateTimeField(auto_now_add=True)
confirmed = models.BooleanField(default=False)
You can add a property to Company that returns the last extrainformation so that company.extrainformation will still work:
#property
def extrainformation(self):
return self.extrainformations.order_by("-version").first()

Django choices with model objects

Yes, this is an assignment, and yes, I've spent some time on it and now I need help.
My task has two models, Server and Client and they are in 1-N relationship. as noted below
# models.py
class Server(models.Model):
name = models.CharField(max_length=255, unique=True, null=False, blank=False)
maximum_clients = models.IntegerField(default=1,null=False, blank=False)
class Client(models.Model):
name = models.CharField(max_length=255, unique=True, null=False, blank=False)
active = models.BooleanField(default=True)
server = models.ForeignKey(Server)
I have created a form with ModelForm which allows me to create a new client on a given server, but the prerequisite of the task is to only offer servers which have free capacity (their maximum_clients is less than actual clients) so this is what I did
#forms.py
from django.db.models import Count
qs = Server.objects.annotate(Count('client'))
server_choices = []
for server in qs:
if server.client__count < server.maximum_clients:
server_choices.append((server,server))
class ClientForm(forms.ModelForm):
name = forms.CharField(label='Client name')
server = forms.ChoiceField(choices=server_choices)
class Meta:
model = Client
fields = ('name','server',)
This approach populates the select with the right servers based on a precondition that I mentioned. However, saving this form produces an error like Cannot assign "u'fifty'": "Client.server" must be a "Server" instance. Fifty is the name of the server with maximum_clients = 50
There is a similar form on admin screens which I also modified to show only available servers and saving there produces the same error.
This is not the right approach. Apart from the error you are seeing, you will also find that your server_choices only update when you restart the webserver, rather than doing so whenever the Server objects themselves change.
You have a foreign key, and need to select from a subset of the related objects. The correct field for that is a ModelChoiceField; this takes a queryset which you can filter in the definition. Since your filter depends on a field in the same model, you need to use an F object.
class ClientForm(forms.ModelForm):
name = forms.CharField(label='Client name')
server = forms.ModelChoiceField(
queryset=Server.objects.annotate(client_count=Count('client')).filter(client_count__lt=F('maximum_clients')
)

Django Model Design

I've previously asked questions for this project, regarding the Django Admin, Inlines, Generics, etc. Thanks again to the people who contributed answers to those. For background, those questions are here:
Django - Designing Model Relationships - Admin interface and Inline
Django Generic Relations with Django Admin
However, I think I should probably review my model again, to make sure it's actually "correct". So the focus here is on database design, I guess.
We have a Django app with several objects - Users (who have a User Profile), Hospitals, Departments, Institutions (i.e. Education Institutions) - all of whom have multiple addresses (or no addresses). It's quite likely many of these will have multiple addresses.
It's also possible that multiple object may have the same address, but this is rare, and I'm happy to have duplication for those instances where it occurs.
Currently, the model is:
class Address(models.Model):
street_address = models.CharField(max_length=50)
suburb = models.CharField(max_length=20)
state = models.CharField(max_length=3, choices=AUSTRALIAN_STATES_CHOICES)
...
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
...
class Hospital(models.Model):
name = models.CharField(max_length=20)
address = generic.GenericRelation(Address)
...
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
address = generic.GenericRelation(Address)
...
Firstly - am I doing it right, with the FK field on each object that has address(es)?
Secondly, is there a better alternative to using Generic Relations for this case, where different objects all have addresses? In the other post, somebody mentioned using abstract classes - I thought of that, but there seems to be quite a bit of duplication there, since the addresses model is basically identical for all.
Also, we'll be heavily using the Django admin, so it has to work with that, preferrably also with address as inlines.(That's where I was hitting an issue - because I was using generic relationships, the Django admin was expecting something in the content_type and object_id fields, and was erroring out when those were empty, instead of giving some kind of lookup).
Cheers,
Victor
I think what you've done is more complicated than using some kind of subclassing (the base class can either be abstract or not).
class Addressable(models.Model):
... # common attributes to all addressable objects
class Hospital(Addressable):
name = models.CharField(max_length=20)
...
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
class Address(models.Model):
street_address = models.CharField(max_length=50)
suburb = models.CharField(max_length=20)
state = models.CharField(max_length=3, choices=AUSTRALIAN_STATES_CHOICES)
...
owner = models.ForeignKey(Addressable)
You should consider making the base class (Addressable) abstract, if you don't want a seperate table for it in your database.