django : get object from foreign key - django

Suppose following model class,
class Bookmark(models.Model):
owner = models.ForeignKey(UserProfile,related_name='bookmarkOwner')
parent = models.ForeignKey(UserProfile,related_name='bookmarkParent')
sitter = models.ForeignKey(UserProfile,related_name='bookmarkSitter')
How can I get sitter objects from owner Objects?
user = UserProfile.objects.get(pk=1)
UserProfile.objects.filter(bookmarkOwner=user)
returns empty tuple, and I cannot specify sitter variable.

I believe you can do something like this, if you want to avoid using a loop:
pks = some_user_profile.bookmarkOwner.values_list('sitter', flat=True)
sitters = UserProfile.objects.filter(pk__in=pks).all()
Alternatively, you might want to experiment with setting up a many-to-many field and using the through parameter. See the Django docs: https://docs.djangoproject.com/en/2.0/ref/models/fields/#manytomanyfield

you should do
objs = Bookmark.objects.filter(owner=user)
# This will return all bookmarks related to the user profile.
for obj in objs:
print obj.owner # gives owner object
print obj.parent # gives parent object
print obj.sitter # gives sitter object
If there is only one Bookmark object for a user profile (no multiple entries). Then you should use .get method instead (which return a single object).
obj = Bookmark.objects.get(owner=user)
print obj.owner
print obj.parent
print obj.sitter

Related

Django, check if an object present in query set using exists() method

I have a below model,
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
head = models.CharField(max_length=255)
authors = models.ManyToManyField(Author)
I have created an object in Entry model, when I try to check if there is any objects in Entry model it shows error as attached image
It is Entry.objects.filter(pk=1).exists(), since .get() is not a QuerySet, but an Entry object in this case. So you check with:
if Entry.objects.filter(pk=1).exists():
# …
else:
# …
Here however, it is probably simpler to work with a try-except clause, and thus work in an EAFP style [wiki]:
try:
entry = Entry.objects.get(pk=1)
print('k')
except Entry.DoesNotExists:
print('false')
If you need the object you can do:
entry = Entry.objects.filter(pk=1).first()
if entry:
# …
else:
# …
The .first() method returns None if the queryset is empty.

OneToOneField Relationship Not Created

I have a form in which I take the data from some of the fields and create a new model object, then assign that newly created object a one-to-one relationship to a preexisting object of a different model. Here is my save method in the form.
def save(self, *args, **kwargs):
super(CustomerProfileForm, self).save(*args, **kwargs)
if self.cleaned_data['street_address']:
if not self.instance.customer.home_location:
home_location = Location()
else :
home_location = self.instance.customer.home_location
home_location.name = 'Home/Apartment'
home_location.street_address = self.cleaned_data['street_address']
home_location.city = self.cleaned_data['city']
home_location.state = self.cleaned_data['state']
home_location.zip_code = self.cleaned_data['zip_code']
self.instance.customer.home_location = home_location
home_location.save()
self.instance.customer.save()
return self.instance
The Location object is being created and populated with the information from the form as I expect, but the OneToOne relationship with the CustomerProfile object (self.instance) is not being assigned. Does anyone know why this might be?
This makes no sense to me. When I print self.instance.customer.home_location right before the end of the save function, the new location is logged to the console, which shows that the relationship is assigned... How does it get unassigned after the save method completes...?
In order to save a relationship, the object needs to have a primary key; and this is only generated after the object is saved.
Therefore, you need to save the object first, before assigning it as a foreign key:
home_location.save()
self.instance.customer.home_location = home_location
# home_location.save() - this line should come before any relationships
# are linked to the object.
self.instance.customer.save()

Django loop over all m2m relations

I need to loop over all m2m relations of the model instance and copy them to new model instance.
source_id=request.GET.get('source_id', 1)
obj = Artist.objects.create(title='New artist')
source_obj = Artist.objects.get(id=source_id)
if source_obj.galleries.count():
obj.galleries = source_obj.galleries.all()
if source_obj.suggested_artists.count():
obj.suggested_artists = source_obj.suggested_artists.all()
Currently i am doing it like this, but i want to loop over all m2m fields and copy the related data to obj.
I want something like:
for m2m_rel in source_obj.m2m_relations:
print geattr(source_obj, m2m_rel).count()
print geattr(source_obj, m2m_rel).all()
Any suggestions?
You can access the m2m relation entries like this:
for field in source_obj._meta.many_to_many:
source = getattr(source_obj, field.attname)
for item in source.all():
# do something with item...
print repr(item)
If you're trying to clone a model intance, you can use a generic clone_objects function like the one below. The function will clone a list of objects and return a new list of cloned objects (with new ID's):
# import Python's copy library
import copy
def clone_objects(objects):
"""
Generic model object cloner function.
"""
def clone(obj):
"""Return an identical copy of the instance with a new ID."""
if not obj.pk:
raise ValueError('Instance must be saved before it can be cloned.')
duplicate = copy.copy(obj)
# Setting pk to None tricks Django into thinking this is a new object.
duplicate.pk = None
duplicate.save()
# ... but the trick loses all ManyToMany relations.
for field in obj._meta.many_to_many:
source = getattr(obj, field.attname)
destination = getattr(duplicate, field.attname)
for item in source.all():
destination.add(item)
return duplicate
if not hasattr(objects,'__iter__'):
objects = [ objects ]
objs = []
for obj in objects:
new_obj = clone(obj)
new_obj.save()
objs.append(new_obj)
return objs
The main part of the "cloning" code is from this snippet: Clone model mixin

Django : Validate data by querying the database in a model form (using custom clean method)

I am trying to create a custom cleaning method which look in the db if the value of one specific data exists already and if yes raises an error.
I'm using a model form of a class (subsystem) who is inheriting from an other class (project).
I want to check if the sybsystem already exists or not when i try to add a new one in a form.
I get project name in my view function.
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def clean(self,project_name):
cleaned_data = super(SubsytemForm, self).clean(self,project_name)
form_subsystem_name = cleaned_data.get("subsystem_name")
Subsystem.objects.filter(project__project_name=project_name)
subsystem_objects=Subsystem.objects.filter(project__project_name=project_name)
nb_subsystem = subsystem_objects.count()
for i in range (nb_subsystem):
if (subsystem_objects[i].subsystem_name==form_subsystem_name):
msg = u"Subsystem already existing"
self._errors["subsystem_name"] = self.error_class([msg])
# These fields are no longer valid. Remove them from the
# cleaned data.
del cleaned_data["subsystem_name"]
return cleaned_data
My view function :
def addform(request,project_name):
if form.is_valid():
form=form.save(commit=False)
form.project_id=Project.objects.get(project_name=project_name).id
form.clean(form,project_name)
form.save()
This is not working and i don't know how to do.
I have the error : clean() takes exactly 2 arguments (1 given)
My model :
class Project(models.Model):
project_name = models.CharField("Project name", max_length=20)
Class Subsystem(models.Model):
subsystem_name = models.Charfield("Subsystem name", max_length=20)
projects = models.ForeignKey(Project)
There are quite a few things wrong with this code.
Firstly, you're not supposed to call clean explicitly. Django does it for you automatically when you call form.is_valid(). And because it's done automatically, you can't pass extra arguments. You need to pass the argument in when you instantiate the form, and keep it as an instance variable which your clean code can reference.
Secondly, the code is actually only validating a single field. So it should be done in a specific clean_fieldname method - ie clean_subsystem_name. That avoids the need for mucking about with _errors and deleting the unwanted data at the end.
Thirdly, if you ever find yourself getting a count of something, iterating through a range, then using that index to point back into the original list, you're doing it wrong. In Python, you should always iterate through the actual thing - in this case, the queryset - that you're interested in. However, in this case that is irrelevant anyway as you should query for the actual name directly in the database and check if it exists, rather than iterating through checking for matches.
So, putting it all together:
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def __init__(self, *args, **kwargs):
self.project_name = kwargs.pop('project_name', None)
super(SubsystemForm, self).__init__(*args, **kwargs)
def clean_subsystem_name(self):
form_subsystem_name = self.cleaned_data.get("subsystem_name")
existing = Subsystem.objects.filter(
project__project_name=self.project_name,
subsytem_name=form_subsystem_name
).exists()
if existing:
raise forms.ValidationError(u"Subsystem already existing")
return form_subsystem_name
When you do form=form.save(commit=False) you store a Subsystem instance in the variable form but the clean method is defined in SubsystemForm. Isn't it?

Django: Adding objects to a related set without saving to DB

I'm trying to write an internal API in my application without necessarily coupling it with the database.
class Product(models.Model):
name=models.CharField(max_length=4000)
price=models.IntegerField(default=-1)
currency=models.CharField(max_length=3, default='INR')
class Image(models.Model):
# NOTE -- Have changed the table name to products_images
width=models.IntegerField(default=-1)
height=models.IntegerField(default=-1)
url=models.URLField(max_length=1000, verify_exists=False)
product=models.ForeignKey(Product)
def create_product:
p=Product()
i=Image(height=100, widght=100, url='http://something/something')
p.image_set.add(i)
return p
Now, when I call create_product() Django throws up an error:
IntegrityError: products_images.product_id may not be NULL
However, if I call p.save() & i.save() before calling p.image_set.add(i) it works. Is there any way that I can add objects to a related object set without saving both to the DB first?
def create_product():
product_obj = Product.objects.create(name='Foobar')
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something', product=product_obj)
return product_obj
Explanation:
Product object has to be created first and then assign it to the Image object because id and name here is required field.
I am wondering why wouldn't you not require to make a product entry in DB in first case? If there is any specific reason then i may suggest you some work around?
EDIT: Okay! i think i got you, you don't want to assign a product to an image object initially. How about creating a product field as null is equal to true.
product = models.ForeignKey(Product, null=True)
Now, your function becomes something like this:
def create_product():
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something')
return image_obj
Hope it helps you?
I got same issue with #Saurabh Nanda
I am using Django 1.4.2. When I read in django, i see that
# file django/db/models/fields/related.py
def get_query_set(self):
try:
return self.instance._prefetched_objects_cache[rel_field.related_query_name()]
except (AttributeError, KeyError):
db = self._db or router.db_for_read(self.model, instance=self.instance)
return super(RelatedManager,self).get_query_set().using(db).filter(**self.core_filters)
# file django/db/models/query.py
qs = getattr(obj, attname).all()
qs._result_cache = vals
# We don't want the individual qs doing prefetch_related now, since we
# have merged this into the current work.
qs._prefetch_done = True
obj._prefetched_objects_cache[cache_name] = qs
That 's make sese, we only need to set property _prefetched_objects_cache for the object.
p = Product()
image_cached = []
for i in xrange(100):
image=Image(height=100, widght=100, url='http://something/something')
image_cached.append(image)
qs = p.images.all()
qs._result_cache = image_cached
qs._prefetch_done = True
p._prefetched_objects_cache = {'images': qs}
Your problem is that the id isn't set by django, but by the database (it's represented in the database by an auto-incremented field), so until it's saved there's no id. More about this in the documentation.
I can think of three possible solutions:
Set a different field of your Image model as the primary key (documented here).
Set a different field of your Production model as the foreign key (documented here).
Use django's database transactions API (documented here).