How to save Django model instances with circular dependency? - django

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.

Related

django multiple models with same foreign key

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.

How to make recursive django admin inline relationships?

I need to create an arbitrarily deep modular structure using Django admin. The nested_admin package has gotten me part of the way there, but there's a key piece of functionality that's still eluding me — the ability to create an object B inline off object A and then create another object A off that instance of object B. Here's a closer look at my models (simplified):
class ChatConditional(models.Model):
triggering_question = models.ForeignKey(
'texts.MessageChain',
on_delete=models.CASCADE,
)
keyword = models.CharField(max_length=50, blank=False, null=False)
keyword_response = models.ForeignKey(
'texts.MessageChain',
related_name='keyword_answer',
on_delete=models.CASCADE,
)
class MessageChain(models.Model):
conditional_trigger = models.ForeignKey(
'texts.ChatConditional',
blank=True, null=True,
)
message = models.TextField(max_length=160, blank=True, null=True)
So in the admin, I want to be able to create a ChatConditional to serve as a response to a certain MessageChain. Then once I create that Conditional, create another MessageChain to send that will potentially link to another Conditional. Here's what my admin models look like:
class SMSConditionalAdmin(nested_admin.NestedStackedInline):
model = ChatConditional
extra = 0
fk_name = "triggering_question"
inlines = [SMSChainAdmin]
class SMSChainAdmin(nested_admin.NestedStackedInline):
model = MessageChain
extra = 0
inlines = [SMSConditionalAdmin]
And now you can likely see the problem: In order to fully define the SMSConditionalAdmin, I need to set up an inline relationship to SMSChainAdmin, and vice versa. I might be missing something obvious, but how can I work around this need for the two admin inlines to recursively refer to one another?
Thanks in advance!

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')
)

Creating and saving non-empty Django model instance as part of subclass instance

I am experiencing a very strange behavior inconsistent with the Django documentation while creating and saving (inserting into DB) model instance. I've already run out of ideas for possible reason and will be very grateful for any suggestions why Django fails to save all fields in these cases.
This class I am using:
class Person(models.Model):
user = models.ForeignKey(User)
phone_number = models.CharField(max_length=20, blank=True)
address = models.CharField(max_length=200, blank=True)
And here's code that does't work, few cases:
# First Case
new_person = Person()
new_person.user = request.user
new_person.phone_number = '111111'
new_person.save(force_insert=True)
# Second One
new_person = Person(user=request.user, phone_number='111111')
new_person.save(force_insert=True)
# Third One
new_person = Person.objects.create(user=request.user, phone_number='111111')
Basing on official Django docs in any case django should create an object and insert it into DB.
In fact the object is successfully created (and all relevant fields are set), but row inserted into DB has only id and user_id fields filled correctly while phone_number field that was also set, remains blank.
There is, however, no problem to access and update all fields of existing (saved earlier) objects.
Removing blank=True from Person class declaration (with proper table alteration) does't change anything.
EDIT:
Problem turned out to be more sophisticated. Full description and solution in my own answer beneath
Ok, I found an explanation....
It has something to do with inheritance, namely further in the code I wanted to create instance of Person's subclass. So there was another class:
class Person(models.Model):
user = models.ForeignKey(User)
phone_number = models.CharField(max_length=20, blank=True)
address = models.CharField(max_length=200, blank=True)
class ConnectedPerson(Person):
connection = models.ForeignKey(AnotherClass)
# etc..
And after creating instance of Person, intending to extend it to ConnectedPerson I made such code:
#creating instance of Person:
person = Person(user=request.user, phone_number='111111')
person.save(force_insert=True)
c_person = ConnectedPerson(id=person.id, connection=instance_of_another_c)
and using ConnectedPerson(id=person.id) was in fact killing previously created Person instance by overwritting it in the DB.
So for anyone not too much experienced in managing inheriting instances: if you need to use earlier created super class instance as part of subclass instance do it this way:
#creating person but not saving it
person = Person(user=request.user, phone_number='111111')
######
#later
######
#creating subclass instance and saving
c_person = ConnectedPerson(user=request.user, connection=instance_of_another_c)
c_person.save()
#saving super class instance as part of subclass instance
person.pk = super(ConnectedPerson, c_person).pk
person.save()

Django - Best way to merge two identical apps

I recently came onto a project in which we have two applications that are virtually identical. We are using Django 1.4 and Postgresql 8.4. Both models have the following:
class Author(models.Model):
person = models.ForeignKey(Person)
book = models.ForeignKey(Book)
order = models.PositiveIntegerField(blank=True,null=True)
institute = models.ForeignKey(Institution,blank=True, null=True)
rank = models.ForeignKey(Rank,blank=True, null=True)
class Institution(models.Model):
name = models.CharField(max_length=200)
parent_institution = models.ForeignKey('self', blank=True, null=True)
location = models.ForeignKey(Location, blank=False, null=False)
type = models.ForeignKey(InstitutionType, blank=False, null=False)
class Person(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
middle_name = models.CharField(max_length=50,blank=True,null=True)
gender = models.CharField(max_length=1,choices=GENDER_TYPE,blank=True,null=True)
class InstitutionType(models.Model):
type = models.CharField(max_length=255)
Is there a way to easily merge the two, either through SQL or through Django? I'm not quite sure what the best approach would be. My only issue is that there is a lot of foreign key references. Is there a good way in which you could change the primary keys of one application's table to be higher than the other application (essentially reassign primary keys in the second table starting where the first table ends) and have them trickle down and then eventually merge the two tables? Any sort of feedback would be much appreciated.
If what Alexander is suggesting to you is not to your linking, you can use django multi-db support to:
Keep both apps running on the same database and models
Use a migration/sync script while you slowly decouple both models so that only one is being used
Write tests :)
Probably the most difficult thing in merging are models.
If you use Django you should use South too. If you don't - try it.
Take first application as a base. Add fields from second application and create schemamigration.
Then move your data with datamigration from second application.
Merge your code from the second app.