In my django app I have models set up similar to these models on the django site - Extra fields on many-to-many relationships. Further down the page, I read
The remove() method is disabled for similar reasons. However, the clear() method can be used to remove all many-to-many relationships for an instance:
If the remove method is disabled then how do I remove an object from a manytomany field? It says that I can use the clear method to remove everything but I only want to remove one specific element from the manytomany field.
You can remove the instance on the intermediary model.
From the example provided in djangoproject:
m_qs = Membership.objects.filter(person=person, group=group) #or some other logic to filter
try:
m = m_qs.get() #assuming queryset returns only 1 element
m.delete()
except:
pass #handle more gracefully
Related
I am rewriting some administration interface to django 2.2, currently using django autocomplete_fields admin feature. Simply said I have ModelAdmin object OrderAdmin, which has nested TabularInline ProductAdmin: variable-length table of products which might be added to order. Each of these ProductAdmin holders just contains ForeignKey to actual product class, with some other attributes.
Now I wonder: where does django store id - ForeignKey - of item selected with autocomplete field? It doesn't mark OPTION in selectbox as selected, and although there is suspicious hidden input field with #cashregisterproduct_set-0-id on page, it doesn't have any value. Or is there some special way how to access it? I was thinking about adding id to __str__ method of model and parsing, but thats just ugly.
Thanks for tip.
EDIT: to make it 100% clear, where from does django get ForeignKey of object selected through autoselect_field, when creating new object from ModelAdmin?
I got misguided thinking that this is managed by django. Selected data might be accessed by using select2 framework:
selected_value = $('.myselectbox').select2().val();
related: https://stackoverflow.com/a/47451658/16268461
When working with a Django Model with multi-table inheritance set-up as in the docs, the admin app cannot add a new "Restaurant" model if a matching "Place" entry exists - the admin app returns "Place with this name already exists".
Django's ModelForm provides methods for form validation, and the Model provides uniqueness validation.
Which is the best place to enable turning the existing Place entry into a Restaurant?
How would you do this?
For Example, a Place(name="hotdogshop", address="bond street") exists, and the user tries to add a Restaraunt( serves_hot_dogs=True, serves_pizza=False, name="hotdogshop", address="bond street" ). The desired end result would be the same as if we had added the "hotdogshop" as a "Restaraunt" to begin with.
An initial hacky workaround is to insert an extra uniqueness check to the model verification, and switch to using django-typed-models so we can recast models.
For example, add the following pseudo-code to your Model.
def _perform_unique_checks(self, unique_checks):
print("Performing custom Recast Unique Check")
try:
# Search for any existing Model you want to replace.
exists = MyModel.objects.get(...)
if exists:
# django-typed-models can be recast without affecting integrity.
exist.recast('myApp.mySubclassedModel')
# Pretend nothing went wrong, so the rest of the save process continues.
return {}
except TwitterHarvestedUser.DoesNotExist:
pass
return super()._perform_unique_checks(unique_checks)
Be careful around how you merge the data from the previous and new model. Djangos save() method will by default end up replacing all the old models fields even if they are unchanged in the new model.
This doesn't work with MyModel.objects.create()
My goal is to create a clone of a queryset and then insert it into the database.
Following the suggestions of this post, I have the following code:
qs_new = copy.copy(qs)
MyModel.objects.bulk_create(qs_new)
However, with this code I run into duplicate primary key error. As for now, I only can come up with the following work-around:
qs_new = copy.copy(qs)
for x in qs_new:
x.id = None
MyModel.objects.bulk_create(qs_new)
Question: Can I implement this code snippet without going through loop ?
Can't think of a way without loop, but just a suggestion:
# add all fields here except 'id'
qs = qs.values('field1', 'field2', 'field3')
new_qs = [MyModel(**i) for i in qs]
MyModel.objects.bulk_create(new_qs)
Note that bulk_create behaves differently depending on the underlying database. With Postgres you get the new primary keys set:
Support for setting primary keys on objects created using
bulk_create() when using PostgreSQL was added.
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create
You should, however make sure that the objects you are creating either have no primary keys or only keys that are not taken yet. In the latter case you should run the code that sets the PKs as well as the bulk_create inside transaction.atomic().
Fetching the values explicitly as suggested by Shang Wang might be faster because only the given values are retrieved from the DB instead of fetching everything. If you have foreign key relations or m2m relations you might want to avoid simply throwing the complex instances into bulk_create but instead explicitly naming all attributes that are required when constructing a new MyModel instance.
Here an example:
class MyModel(Model):
name = TextField(...)
related = ForeignKeyField(...)
my_m2m = ManyToManyField(...)
In case of MyModel above, you would want to preserve the ForeignKey relations by specifying related_id and the PK of the related object in the constructor of MyModel, avoiding specifying related.
With m2m relations, you might end up skipping bulk_create altogether because you need each specific new PK, the corresponding original PK (from the instance that was copied) and the m2m relations of that original instance. Then you would have to create new m2m relations with the new PK and these mappings.
# add all fields here except 'id'
qs = qs.values('name', 'related_id')
MyModel.objects.bulk_create([MyModel(**i) for i in qs])
Note for completeness:
If you have overriden save() on your model (or if you are inheriting from 3rd party with custom save methods), it won't be executed and neither will any post_save handlers (yours or 3rd party).
I tried and you need a loop to set the id to None, then it works. so finally it may be like this:
qs_new = copy.copy(qs)
for q in qs_new:
q.id = None
# also, you can set other fields if you need
MyModel.objects.bulk_create(qs_new)
This works for me.
I was trying to update certain Article's created_by and edited_by which have ManytoMany relation with a user and add another user to it.
I came across
MyModel.objects.filter(pk=some_value).update(field1='some value')
As a way to update with a single query is there a way to update ManytoMany with a single query too?
Article.objects.filter(Q(created_by__in=[deleted_user_id])| Q(edited_by__in=[deleted_user_id])).update(?)
What should I replace "?" with?
Assume that I need to add "replacement_user_id" to all those article which the filter returns.
I guess we can do by querying the "through" model maybe.
This is not possible using update method:
You can only set non-relation fields and ForeignKey fields using this
method.To update a non-relation field, provide the new value as a
constant. To update ForeignKey fields, set the new value to be the new
model instance you want to point to.
Learn more here
Update:
You can add multilple objects like:
articles = Article.objects.filter(Q(created_by__in=[deleted_user_id])| Q(edited_by__in=[deleted_user_id]))
created_by_objs = User.objects.filter(...)
edited_by_objs = User.objects.filter(...)
for article in articles:
#article.created_by.clear() uncomment if needed
article.created_by.add(*created_by_objs)
#article.edited_by.clear() uncomment if needed
article.edited_by.add(*edited_by_objs)
Learn more here
In a django form that has a manyToMany field - is there a database call to retrieve each object in the list when you're adding a new entry? Is this necessary/ wasteful?
For example:
class MyForm(ModelForm):
likes = forms.ModelMultipleChoiceField(queryset=Videos.objects.all())
....
the form is submitted with a list of project id's. within the clean() method likes becomes a list of Video objects. However you can do:
self.instance.likes.add() ...without ever having to get the objects.
...
is it wasteful that the objects are being retrieved first before updating the relationship? if not, why?
I would say that every time you save the form, it would be evaluating the queryset you passed it to check that each ID is within that queryset. if you wish to optimize the behavior, try subclassing ModelMultipleChoiceField and removing the checks/making them more streamlined.