Django: removing m2m relation in object - python

I just started playing with Django, I love it! I am still working my way around with the Django ORM though...
At the moment I have a model Shift with a m2m relationship with users:
class Shift(models.Model):
users = models.ManyToManyField(User)
I want to define a view that checks if a M2M relationship exists, if it does, it removes this relationship. This is where I am stuck: I am able to lookup if a relationship exists, but I am not able to remove it. What is wrong with my code?
def remove_if_exists(request, shift_id, username):
shift = get_object_or_404(Shift, pk=shift_id)
if shift.users.filter(username=username).exists()
shift.users.remove(username)

The trouble with your code is that the relationship is not with a username, but with a User object. So your call to remove should have a User object as its argument. You need to actually get the relevant user from the db first, then call remove with that object.
However, there is a shortcut: remove does not raise an error if the object is not in the related set, so you can skip the exists call. That gives just:
user = User.objects.get(username=username)
shift = get_object_or_404(Shift, pk=shift_id)
shift.users.remove(user)

Some stupid syntax mistake of my own, should be:
shift.users.remove(User.objects.get(username=username))

Related

Django import-export, only export one object with related objects

I have a form which enables a user to register on our website. Now I need to export all the data to excel, so I turned towards the import-export package. I have 3 models, Customer, Reference and Contact. The latter two both have a m2m with Customer. I also created Resources for these models. When I use Resource().export() at the end of my done() method in my form view, it exports all existing objects in the database, which is not what I want.
I tried googling this and only got one result, which basically says I need to use before_export(), but I can't find anywhere in the docs how it actually works.
I tried querying my customer manually like:
customer = Customer.objects.filter(pk=customer.id)
customer_data = CustomerResource().export(customer)
which works fine but then I'm stuck with the related references and contacts: reference_data = ReferenceResource().export(customer.references) gives me an TypeError saying 'ManyRelatedManager' object is not iterable. Which makes sense because export() expects an queryset, but I'm not sure if it's possible getting it that way.
Any help very appreciated!
One way is to override get_queryset(), you could potentially try to load all related data in a single query:
class ReferenceResource(resources.ModelResource):
def __init__(self, customer_id):
super().__init__()
self.customer_id = customer_id
def get_queryset(self):
qs = Customer.objects.filter(pk=self.customer.id)
# additional filtering here
return qs
class Meta:
model = Reference
# add fields as appropriate
fields = ('id', )
To handle m2m relationships, you may be able to modify the queryset to add these additional fields.
This isn't the complete answer but it may help you make progress.

How can I assign a model to all users upon creation in Django?

I have this model:
models.py
class Upload_model(models.Model):
content=models.CharField(max_length=200)
user_name=models.ForeignKey(User)
forms.py
class upload_form(ModelForm):
class Meta:
model=Upload_model
fields=[“content”]
views.py
I'm trying to write a function that iterates the creation of the Upload_model to all Users independently. But it won't work.
def upload_all_user_models(request):
if request.method==”POST”:
form=upload_form(request.POST, request.FILES)
instance=form.save(commit=False)
instance.user_name=User.objects.all()
return redirect(“upload_all_user_models”)
else:
form=upload_form()
return render(request,”uploadpage.html”,{“form”:form})
I want the Upload_model to be assigned to all users upon creation but I seem to be getting a ValueError. How can I solve this?
I believe when you use instance.user_name=User.objects.all(), you're assigning the instance.user_name to a queryset of User rather than each individual users (i.e. instance.user_name=[queryset<id=1, id=2>]). You need to set up conditional statements that determine if each user doesn't have the Upload_model, then add and save Upload_model to the user.
What you're trying to create is going to be complicated due to the order in which model gets created first: the upload_model or the user.
Let's say you have 5 users and you create an Upload_model, saving the Upload_model to the 5 users. What's going to happen if user #6 comes along?
I'm sure there's a way you can configure the code to make it how you want it, but at the end of it all, your question is quite vague but all in all you're getting a ValueError because you're trying to assign user_name not to each user, but to a list of users.

updating user profile fields django

Should be a simple answer but I can't figure out what's wrong here...
I have a user profile with a couple of simple fields. I'm trying to update them like so:
if data['dob'] != None:
request.user.profile.dob = data['dob']
request.user.profile.save()
This doesn't seem to have any effect at all though.
p.s. i am using a nice little trick in my UserProfile class that looks like this:
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
Could this be part of the problem?
Think about what happens in your code.
If there's a dob in your data, you call request.user.profile. This calls your property, which makes a request to the database and gets or creates a Profile instance.
Next, you call request.user.profile again. Guess what this does? Makes a fresh call to the database, and gets an instance of the Profile again. But of course this is a new instance, even though it's referring to the same database row, so it won't have the value for dob you just set on the last version.
Now, potentially you could solve this by storing the profile in a local variable:
profile = request.user.profile
profile.dob = data['dob']
profile.save()
But to be honest, I'd drop the whole hacking around with the profile property. It's going to cause you all sorts of problems.
It might be easier to use the suggested method of tying a profile to a django user:
https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
In the meantime, remove the [0] at the end of the UserProfile.objects.get_or_create(user=u) as that method only returns a single object regardless

django: Optimal way to select manytomany object with a particular attribute

I have a model app which has many to many association with users. Here is the code.
class app(models.Model):
users = models.ManyToManyField(UserProfile)
Now I want to check if a Django user with particular 'id' exits in this app's user list. After some hits and trials, I got to a solution.
def user_exists(self,user_obj):
userset = self.app.users.all()
for userprof in userset:
if userprof.user == user_obj:
return True
return False
How can I improve upon this?
That is very inefficient: it gets all related users and iterates through.
A ManyToManyField returns a queryset. So you can use the normal queryset filtering methods, to do all that in a single command:
return self.app.users.filter(user=user_obj).exists()
Note this uses the exists() method to return a bool directly from the database, rather than evaluating the actual objects.

Why do I need to save this model before adding it to another one?

In django, I'm trying to do something like this:
# if form is valid ...
article = form.save(commit=False)
article.author = req.user
product_name = form.cleaned_data['product_name']
try:
article.product = Component.objects.get(name=product_name)
except:
article.product = Component(name=product_name)
article.save()
# do some more form processing ...
But then it tells me:
null value in column "product_id" violates not-null constraint
But I don't understand why this is a problem. When article.save() is called, it should be able the create the product then (and generate an id).
I can get around this problem by using this code in the except block:
product = Component(name=product_name)
product.save()
article.product = product
But the reason this concerns me is because if article.save() fails, it will already have created a new component/product. I want them to succeed or fail together.
Is there a nice way to get around this?
The way the Django ManyToManyField works is that it creates an extra table. So say you have two models, ModelA and ModelB. If you did...
ModelA.model_b = models.ManyToManyField(ModelB)
What Django actually does behind the scenes is it creates a table... app_modela_modelb with three columns: id, model_a_id, model_b_id.
Hold that thought in your mind. Regarding the saving of ModelB, Django does not assign it an ID until it's saved. You could technically manually assign it an ID and avoid this problem. It seems you're letting django handle that which is perfectly acceptable.
Django has a problem then doing the M2M. Why? If ModelB doesn't have an id yet, what goes in the model_b_id column on the M2M table? The error for null product_id is more than likely a null constraint error on the M2M field, not the ModelB record id.
If you would like them to "succeed together" or "fail together" perhaps it's time to look into transactions. You, for example, wrap the whole thing in a transaction, and do a rollback in the case of a partial failure. I haven't done a whole lot of work personally in this area so hopefully someone else will be of assistance on that topic.
You could get around this by using :
target_product, created_flag = Component.objects.get_or_create(name=product_name)
article.product = target_product
as I'm pretty sure get_or_create() will set the id of an object, if it has to create one.
Alternatively, if you don't mind empty FK relations on the Article table, you could add null=True to the definition.
There's little value in including a code snippet on transactions, as you should read the Django documentation to gain a good understanding.

Categories

Resources