Django add many to many relationship from _meta.get_field - python

So I have the following
b = Brand.objects.create(**model_fields_and_values)
b.save()
and then I try to associate that entry with
b._meta.get_field("myfield").add(value3)
and I get the error 'ManyToManyField' object has no attribute 'add'
How can I create an association using a string and not the field ???
I dont want to use b.myfield.add(value3)

getattr allows you to get an attribute using the attribute name:
getattr(b, 'myfield').add(value3)

Related

Django ._meta and adding to ManyToMany fields

I haven't had much luck finding other questions that helped with this, but apologies if I missed something and this is a duplicate.
I'm trying to add to some ManyToMany fields, without having to explicitly type out the names of the fields in the code (because the function I'm working on will be used to add to multiple fields and I'd rather not have to repeat the same code for every field). I'm having a hard time using ._meta to reference the model and field objects correctly so that .add() doesn't throw an "AttributeError: 'ManyToManyField' object has no attribute 'add'".
This is simplified because the full body of code is too long to post it all here, but in models.py, I have models defined similar to this:
class Sandwich(models.Model):
name = models.CharField(max_length=MAX_CHAR_FIELD)
veggies = models.ManyToManyField(Veggie)
meats = models.ManyToManyField(Meat)
class Veggie(models.Model):
name = models.CharField(max_length=MAX_CHAR_FIELD)
class Meat(models.Model):
name = models.CharField(max_length=MAX_CHAR_FIELD)
Once instances of these are created and saved, I can successfully use .add() like this:
blt = Sandwich(name='blt')
blt.save()
lettuce = Veggies(name='lettuce')
lettuce.save()
tomato = Veggies(name='tomato')
tomato.save()
bacon = Meat(name='bacon')
bacon.save()
blt.veggies.add(lettuce)
blt.veggies.add(tomato)
blt.meats.add(bacon)
But if I try to use ._meta to get blt's fields and add to them that way, I can't. ie something like this,
field_name='meats'
field = blt._meta.get_field(field_name)
field.add(bacon)
will throw "AttributeError: 'ManyToManyField' object has no attribute 'add'".
So, how can I use ._meta or a similar approach to get and refer to these fields in a way that will let me use .add()? (bonus round, how and why is "blt.meats" different than "blt._meta.get_field('meats')" anyway?)
Why do you want to do
field = blt._meta.get_field(field_name)
field.add(bacon)
instead of
blt.meats.add(bacon)
in the first place?
If what you want is to access the attribute meats on the blt instance of the Sandwich class because you have the string 'meats' somewhere, then it's plain python you're after:
field_string = 'meats'
meats_attribute = getattr(blt, field_string, None)
if meats_attribute is not None:
meats_attribute.add(bacon)
But if your at the point where you're doing that sort of thing you might want to revise your data modelling.
Bonus round:
Call type() on blt.meats and on blt._meta.get_field(field_name) and see what each returns.
One is a ManyToManyField, the other a RelatedManager. First is an abstraction that allows you to tell Django you have a M2M relation between 2 models, so it can create a through table for you, the other is an interface for you to query those related objects (you can call .filter(), .exclude() on it... like querysets): https://docs.djangoproject.com/en/4.1/ref/models/relations/#django.db.models.fields.related.RelatedManager

Passing an Object into Include Template Tag Django

I am developing a website in django and I have encountered a problem.
I am using an 'include' template tag and I am trying to pass an object into the tag. But whenever I pass it in, it is passing in the dunder str method of the object.
{% include 'function/comment' object=answer %}
'answer' is the object I want to pass in (fyi it is a model object)
Instead of passing the answer object it is passing the dunder str method.
Is there a way to solve this?
Thanks in advance.
Instead of passing the object itself, I passed the id (answer.id) and then I did a query for that id which got the object on the backend side.

How do i find mongodb collection and update multiple fields using pymongo

This is the code snippet
send = mongo.db.subscription
send.find().update({'$set':{'status': "expired"}})
this is the output i get
send.find().update({'$set':{'status': "expired"}})
AttributeError: 'Cursor' object has no attribute 'update'
The syntax you want is:
send.update_many({<filter_condition>}, {'$set':{'status': "expired"}})

(Django 1.10) referring to properties of an object referred to by ForeignKey

In my app each object Kurs needs to have a field 'prowadzacy' specifying the user to which the given Kurs belongs. Therefore, my models.py has the following:
class Kurs(models.Model):
prowadzacy = models.ForeignKey(User)
I also need to know the first name of the user in possession of the given Kurs. In shell, the following works:
>>> k=Kurs.objects.get(id=1)
>>> k
<Kurs: Wprowadzenie do epi 2>
>>> k.prowadzacy
<User: leszekwronski>
>>> k.prowadzacy.first_name
u'Leszek'
Eventually I need to have a field in my Kurs object containing an outcome of a procedure transforming the possessor's first and last names.
As a first step, I want to add a field containing just the first name. However, when I modify my models.py to contain the following:
class Kurs(models.Model):
prowadzacy = models.ForeignKey(User)
imie = prowadzacy.first_name
then I get the following error:
AttributeError: 'ForeignKey' object has no attribute 'first_name'
I tried using 'self.prowadzacy.first_name' instead of just 'prowadzacy.first_name', but to no avail:
NameError: name 'self' is not defined
What am I doing wrong? After reading this I suspect that I cannot refer to the name of a field in a model until the definition of that particular model is finished. However, 1) I'm not sure and would be grateful for a decisive 'yes' or 'no' and 2) if that's the case, how I can refer to "the first name of the particular user which is the value of a different field in the model I am defining now"?
A "procedure" means a method. If you want your Kurs model to be able to display the full user name, write a method to do it.
class Kurs(models.Model):
...
def full_user_name(self):
return u'{} {}'.format(self.prowadzacy.first_name, self.prowadzacy.last_name)

Python what does it mean "AttributeError: 'unicode' object has no attribute 'has_key' "

I would like to ask what does it mean "AttributeError: 'unicode' object has no attribute 'has_key'"
Here is the full stack trace:
Traceback (most recent call last):
File "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\webapp\__init__.py", line 509, in __call__
handler.post(*groups)
File "D:\Projects\workspace\foo\src\homepage.py", line 71, in post
country=postedcountry
File "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\db\__init__.py", line 656, in __init__
prop.__set__(self, value)
File "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\db\__init__.py", line 2712, in __set__
value = self.validate(value)
File "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\db\__init__.py", line 2742, in validate
if value is not None and not value.has_key():
AttributeError: 'unicode' object has no attribute 'has_key'
Let me describe a little bit more about the situation:
First I created the models.py that has the db.Model for CMSRequest which has an attribute country that reference to the CMSCountry class
class CMSRequest(db.Model):
country = db.ReferenceProperty(CMSCountry, required=True)
class CMSCountry(db.Model):
name = db.StringProperty(required=True)
Then, I created a bulkloader class to import the data into the CMSCountry
In the form, user can select the country from the drop down list box, the results are posted back and save to the CMSRequest object
def post(self):
postedcountry = self.request.get('country')
cmsRequest = models.CMSRequest(postedcountry)
Maybe I have found the solution to my question, it is because I have not converted the posted key of CMSCountry back to save to the CMSRequest.
Thank you everyone!
In this line:
if value is not None and not value.has_key():
value is a unicode string. It looks like the code is expecting it to be a db.Model,
(From what I can see, has_key is a method of db.Model, as well as a method of Python dictionaries, but this must be the db.Model one because it's being called with no arguments.)
Are you passing a string to a GAE API that expects a db.Model?
Your problem is that postedcountry is a string and not a country object. Values retrieved from self.request.get are the string values of variables passed by the browser.
You need to look up a country object using some GQL. Exactly how you do that will depend on what exactly the country field of your HTML form is returning , Object Key?, country name?
def post(self):
postedcountry = self.request.get('country')
# <-------- Need to look up a country here !!!!!
cmsRequest = models.CMSRequest(country=postedcountry)
Note: normally "mapping" types in Python (dictionaries and dictionary like classes ... such as various types of dbm (indexed file) and some DBMS/ORM interfaces ... will implement a has_key() method.
Somehow you have gotten a Unicode (string) object into this statement when you were expecting to have some sort of dictionary or other mapping object reference.
In general AttributeError means that you have tangled up your object bindings (variable assignments). You've given a name to some object other than the type that you intended. (Or sometimes it means you have a typo like ".haskey()" instead of has_key() ... etc).
BTW: the use of has_key() is somewhat dated. Normally it's better to test your containers with the Python in operator (which implicitly calls __contains__() --- and which works on lists, strings, and other sequences as well as mapping types).
Also value.has_key() would raise an error even if value were a dictionary since the .has_key() method requires an argument.
In your code I would either explicitly test for if postedcountry is not None: ... or I'd supply your .get() with an (optional) default for "postedcountry."
How do you want to handle the situation where request had no postedcountry? Do you want to assume it's being posted from some particular default? Force a redirection to some page that requires the user to supply a value for that form element? Alert the Spanish Inquisition?
If you read the traceback, it'll tell you exactly what is going on:
if value is not None and not value.has_key():
AttributeError: 'unicode' object has no attribute 'has_key'
What this says is the the value variable which you're using doesn't have the has_key attribute. And, what it's telling you is that your value variable isn't a dictionary, as it looks like you're expecting...instead, it's a unicode object, which is basically a string.
You're attempting to set a string to a ReferenceProperty. The 'country' field of CMSCountry is a db.ReferenceProperty, which takes a db.Key or a CMSCountry object, but you're attempting to set it to a string.
You should be doing something like this:
def post(self):
postedcountry = self.request.get('country')
country = models.CMSCountry.all().filter('name =', postedcountry)
if not country:
# Couldn't find the country
else:
cmsRequest = models.CMSRequest(country=country)
Please do file a bug about the rather unhelpful error message, though.
The dictionary object's has_key() method is deprecated in 3.0 - use the "in" expression instead. If you are using the old library in 3.x, you must make code changes to accommodate it.

Categories