Updating Django's ManytoMany after filter - django

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

Related

Django: Check at model level if anything in ManyToMany field before saving

There's a lot of questions worded similarly, but every single one I've seen is somebody trying to get some kind of data through a ManyToMany relationship before saving it. I'm not trying to use the relationship at all before saving, I just want to see if the user put anything there or not.
My model has a ForeignKey field pointing to a parent model, and two ManyToMany fields pointing to other models, but I only want users to be able to use one M2M field or the other, not both. This model is being edited through the admin as an inline on its parent.
models.py
class ProductSpecial(models.Model):
# name, slug, image, etc
class ProductSpecialAmount(models.Model):
special = models.ForeignKey(ProductSpecial, related_name='amounts', on_delete=models.CASCADE)
amount = models.IntegerField()
brands = models.ManyToManyField(ProductBrand, related_name='specials', blank=True)
lines = models.ManyToManyField(ProductLine, related_name='specials', blank=True)
admin.py
class ProductSpecialAmountInline(admin.StackedInline):
model = ProductSpecialAmount
# fieldsets, etc
#admin.register(ProductSpecial)
class ProductSpecialAdmin(admin.ModelAdmin):
inlines = [ProductSpecialAmountInline]
# fieldsets, etc
I only want users to be able to choose from brands or lines, but not both, and I would like to validate this before save and throw a validation error if necessary. My initial attempt was to just do...
class ProductSpecialAmount(models.Model):
# ...
def clean(self):
if self.brands and self.lines:
raise ValidationError('Please choose either brands or lines, not both', code='invalid')
...but that throws ValueError: "<ProductSpecialAmount: ProductSpecialAmount object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.
I get that I can't actually query the related ProductBrand or ProductModel objects before this object is saved, but I don't actually want any data from those objects right now, I just want to know if the user left either of the fields blank or not, and am wondering if that's possible to see at the model level.
Whether you actually want to use the data from a field or just see if it is blank, the problem is caused by referencing the m2m field in any way before saving the object. I had a similar problem which I fixed using a custom form as per: https://stackoverflow.com/a/7986937/19837155
This might be more difficult when you're using inlines, but it may be the easiest way to solve your problem.

Where is ID saved in django ModelAdmin autocomplete_fields?

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

Is there any possibility to add fields inside the fields of a Model

Assume I have a model named MyModel and I have a Field Named field Now I want to add three more fields inside the prescription like one field_a , field_b and field_c .
Does Django Allow that in any way or we have to make another model for this purpose and then link with Foreign Key to MyModel?
Well I think it is an idea that could lead to some really hard to maintain code and should be well thought through.
That aside If you have a look at:
https://docs.wagtail.io/en/stable/topics/streamfield.html
It's a special field meant for cms applications that uses json in the field to model dynamic fields and validations for it.
Also if you run postgres this might help. https://docs.djangoproject.com/en/3.2/ref/contrib/postgres/fields/#jsonfield

django multi-table inheritance validation with the admin app

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

Django how to delete from ManyToManyField with extra field?

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