Problems saving Django ORM object with a many to one relationship - python

I have this model:
class Answer(models.Model):
order = models.IntegerField()
question_key = models.IntegerField()
answer_index = models.IntegerField()
user_session = models.ForeignKey(
UserSession, on_delete=models.CASCADE, related_name="answers"
)
user_session cannot be None and I'd love to keep it that way.
This is how I'm trying to save an Answer object:
answer = Answer(
question_key=question_key,
answer_index=answer_index,
user_session=user_session,
order=answer_order,
)
answer.save()
But I get the error: ValueError: save() prohibited to prevent data loss due to unsaved related object 'user_session'.
My research suggests that I need to save the Answer object prior to adding the user_session to it. However, I can't do that if I would like to preserve the not null constraint on the Answer model.
Is there a better way to solve this or should I just allow Answer.user_session to be nullable?

That error is not about unsaved Answer, but about unsaved user session. You need to save all related objects (in that case that is user_session) before referencing it in other objects as foreign key, because object can not be referenced when it does not have an ID (and it does not have it until saved to database)

You didn't save the UserSession object (user_session), hence the error; it's a Many-to-One relationship so you can directly use the UserSession object as the value of user_session field while creating an instance of Answer.
So first save the UserSession object and then refer it directly in Answer model instantiation.
For example:
user_session.save()
answer = Answer(
question_key=question_key,
answer_index=answer_index,
user_session=user_session,
order=answer_order,
)
answer.save()
Also you're not doing anything after instantiating Answer, so you can use create directly:
answer = Answer.objects.create(
question_key=question_key,
answer_index=answer_index,
user_session=user_session,
order=answer_order,
)

Related

how to create a relation model with forms?

Hey i need to create a model from form. It's different way if i need to create a some another model, that have a relations with created object model from form. its must work like that -> i come on site, get form for create a model object and save new object. an then -> i have another model with relations with model from form. and i need, to create relation model object automaticly - when django taked new object from forms.
tried any can help me. i make it this way, but this time i have problem. -> i have manytomany field in my relation model, and i have manytomany field ->users from form. and i cant create a relation model with this instance :( Traceback:
TypeError at /ru/center/add/
Direct assignment to the forward side of a many-to-many set is prohibited. Use users_center.set() instead.
but i tired to try it(( help please, how i may do it?
views.py
for x in form['users_center'].data:
name_center = form['name_center'].data
fullname_center = form['fullname_center'].data
ogrn = form['ogrn_center'].data
head_center = form['head_center'].data # user id
many_users = form['users_center'].data
user_center = x # user id
new_center = Center.objects.create(name_center=name_center,
fullname_center=fullname_center,
ogrn_center=ogrn,
head_center=User.objects.get(id=head_center),
users_center=User.objects.get(id=int(x)))
new_center.save()
models.py
users_center = models.ManyToManyField(User,
related_name='center',
# through=CenterDetails,
default=None, blank=True,
verbose_name=_("Сотрудники"))
There is a join table implied by the many-to-many relationship between two models. The error is letting you know that you must set the relationship after creating a User object instead of trying to assign the relationship while creating the User object. You can use set() or add() to do this.
So try doing:
new_center = Center.objects.create(
name_center=name_center,
fullname_center=fullname_center,
ogrn_center=ogrn,
head_center=User.objects.get(id=head_center),
)
users_center=User.objects.get(id=int(x))
new_center.users_center.add(users_center)
Additionally it may be useful to rename your many to many field as a plural to indicate the relationship. Maybe users_centers instead. Since it seems like users can have many centers, and centers can have many users. That's up to you though, not required for it to work.

Django setting many_to_many object while doing a bulk_create

I am using Django 1.9 and am trying the bulk_create to create many new model objects and associate them with a common related many_to_many object.
My models are as follows
#Computational Job object
class OT_job(models.Model):
is_complete = models.BooleanField()
is_submitted = models.BooleanField()
user_email = models.EmailField()
#Many sequences
class Seq(models.Model):
sequence=models.CharField(max_length=100)
ot_job = models.ManyToManyField(OT_job)
I have thousands of Seq objects that are submitted and have to be associated with their associated job. Previously I was using an iterator and saving them in a for loop. But after reading realized that Django 1.9 has bulk_create.
Currently I am doing
DNASeqs_list = []
for a_seq in some_iterable_with_my_data:
# I create new model instances and add them to the list
DNASeqs_list.append(Seq(sequence=..., ))
I now want to bulk_create these sequence and associate them with the current_job_object.
created_dnaseqs = Seq.objects.bulk_create(DNASeqs_list)
# How do I streamline this part below
for a_seq in created_dnaseqs:
# Had to call save here otherwise got an error
a_seq.save()
a_seq.ot_job.add(curr_job_obj)
I had to call "a_seq.save()" in for loop because I got an error in the part where I was doing "a_seq.ot_job.add(curr_job_obj)" which said
....needs to have a value for field "seq" before this many-to-many relationship can be used.
Despite reading the other questions on this topic , I am still confused because unlike others I do not have a custom "through" model. I am confused with how best to associate the OT_Job with many Seqs with minimal hits to database.
From the docs https://docs.djangoproject.com/en/1.9/ref/models/querysets/#bulk-create:
If the model’s primary key is an AutoField it does not retrieve and set the primary key attribute, as save() does.
It does not work with many-to-many relationships.
bulk_create literally will just create the objects, it does not retrieve the PK into the variable as save does. You would have to re-query the db to get your newly created objects, and then create the M2M relationships, but it sounds like that would not be appropriate and that your current method is currently the best solution.

Django Integrity error _id may not be null, ForeignKey id assignment

I'm having issues understanding the way Django (v1.6.5) assigns the id to the different objects when saving. Taking the minimal example:
#models.py
class Book(models.Model):
title = models.CharField(max_length=10)
class Page(models.Model):
number = models.SmallIntegerField()
book = models.ForeignKey(Book)
The following view throws "IntegrityError,book_id may not be NULL" when saving my_page, however I would tend to say book_id does exist since save() has been called for the book at that stage.
#view.py
my_book = Book(title="My book")
#solution1 : having my_book.save() here
my_page = Page(number = 1, book = my_book)
my_book.save()
print("book id",my_page.book.id) #book.id does exist at that point!
#solution2: my_page.book = my_book
my_page.save() #throws the IntegrityError exception
There are some easy solutions to make the code above work but I would like to know what is wrong with the first approach. Am I missing something or is it a glitch/limitation in the way Django handles ForeignKeys?
You should save the book before setting my_page.book = book.
The behaviour you're experiencing is described by ticket 10811.
I see your point, but the current behavior seems more explicit. my_book is just a Python object, and all of its attributes (including id) can change. So it seems safer to assume that the user wants the value that exists at instantiation time.
For example, the Django idiom for copying a database row involves reusing the same object to represent more than one model instance. In your case that might look like:
my_book = Book(title="My book")
my_page = Page(number=1, book=my_book)
my_book.save()
my_book.id = None
my_book.save() # copy the book to a new row with a new id
my_page.save()
So which book should my_page point to? I think the developers are right to require you to be explicit here. The solution to the associated ticket will be even more direct in that you will get a ValueError when trying to instantiate my_page if my_book hasn't yet been saved.
If there is a foreign key in a model then you need to give the whole object in order to store a record in it. For instance if you want to save a number with book in Page model, so you need to give book foreign key a book object just because its not a normal field but its a foreign key to the book model.
It can be done like following
my_page = Page(title="my title", book=Book.objects.get(pk=id))
So coming to conclusion you cannot insert a record simply by giving strings to the foreign keys, you must give that object to which foreign key is pointing. Hope that helps

Django get_or_create does not return a usable Model object in clean method of ModelForm

Hello,
I have bound a ModelForm to one of my model that contains a ForeignKey to another model everything driven by a CreateView. What I want to achieve is to create the model object corresponding to the foreign key if it doesn't exist before the form is overall validated and the final object created in database.
Below the models I use:
class UmsAlerting(models.Model):
alert_id = models.IntegerField(primary_key=True, editable=False)
appli = models.ForeignKey('UmsApplication')
env = models.ForeignKey('UmsEnvironment')
contact = models.ForeignKey('UmsContacts')
custom_rule = models.ForeignKey('UmsCustomRules', null=True, blank=True)
class UmsApplication(models.Model):
appli_id = models.IntegerField(primary_key=True)
trigram_ums = models.CharField(max_length=4L)
class UmsContacts(models.Model):
contact_id = models.IntegerField(primary_key=True)
mail_addr = models.CharField(max_length=100L)
class UmsEnvironment(models.Model):
env_id = models.IntegerField(primary_key=True)
env_name = models.CharField(max_length=5L)
The model bound to the form is UmsAlerting. The model object I want to create if it doesn't exist is UmsContacts. I managed to use the field's clean method in my ModelForm of the contact field and use the get_or_create method like below:
def clean_contact(self):
data = self.cleaned_data['contact']
c, _ = UmsContacts.objects.get_or_create(mail_addr=data)
return c
It perfectly works when the contact is already in the database but when it needs to be created my form return a ValidationError on the contact field saying "This field cannot be null". If I submit the same form a second time without changing anything the UmsAlerting object is well created with no validation error.
My guess is that, for a reason I don't get, when get_or_create is used to create a UmsContacts object it cannot be used to create the new UmsAlerting object. So in clean_contact method the get is working and returns the UmsContacts object but the create part doesn't. It'd be like the UmsContacts object is saved when the whole form is validated but not before as I'd want it to.
Anyone could help me find out what is the problem ? Is using the clean method not the best idea ? Is there another strategy to use to take around this problem ?
Thanks in advance for your help.
It's probably because the object you are creating expects value for contact_id. If you use contact_id field for just setting object id -then you do not have to create it at all. Django takes care of Id's automatically.
Also. field clean method should return cleaned data not object. That creates whole lot more problems on its own.

Work with instance in session before save in database

I have a system with several steps. Each step increments one single object instance.
I want to save the instance in db only in the final step, on others just update the instance I saved in the session.
My model class seems like this:
class Proposta(models.Model):
Modelo = models.ForeignKey("ModeloVersao", verbose_name="Modelo")
Pacotes = models.ManyToManyField("PacoteModelo", null=True, blank=True)
Opcionais = models.ManyToManyField("ItemModelo", null=True, blank=True)
RevestimentoInterno = models.ForeignKey("RevestimentoInternoModelo", verbose_name="Revestimento Interno")
Cor = models.ForeignKey("CorModelo")
CorSecundaria = models.ForeignKey("CorModeloSecundaria", verbose_name="Cor secundária", null=True, blank=True)
Data = models.DateTimeField(auto_now_add = True)
Status = models.CharField("Status", choices=STATUS_PROPOSTA, max_length=10)
Cliente = models.ForeignKey("Cliente")
Here's my problem:
When I try to add or retrieve m2m fields it obviously throws a ValueError with the message 'Proposta' instance needs to have a primary key value before a many-to-many relationship can be used.
I successfully got the wanted result by creating my obj instance with pk=0 but I'm sure it isn't the best way, if there is.
Do exist a way of doing that without cheating like this.
Any help would be great.
Thanks
You might find the answers to this question helpful.
Summary for quick reference:
Use ModelForms - Based on the ModelForms documentation
Save it to DB, but use a status field - I think this is less than ideal
I might add that the documentation specifically explains how to deal with M2M fields, in the section that explains The save() method.
Of those, I recommend using ModelForms. Hopefully this helps!

Categories