How to reference object in class view in django - python

In my models, I have both tasks and projects. What I'm trying to do is add related tasks into the context of the page in which projects are updated.
The error I'm getting is:
type object 'project' has no attribute 'id'
In models.py I have the task class:
class task(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200)
....
related_project = models.ForeignKey(project, blank=True, null=True)
Also in models.py, I have the project class:
class project(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200)
...
The view I'm using to update the Project object is:
class ProjectUpdate(UpdateView):
"""
This will be used to view and update projects
"""
template_name = 'tasks/update-project.html'
model = project
fields = ['name','status', 'purpose', 'vision', 'big_steps', 'context', 'priority', 'due_date', 'related_project']
def get_context_data(self, **kwargs):
"""
This puts the tasks related to the particular project in context
"""
# Call base implementation first to get a context
context = super(ProjectUpdate, self).get_context_data(**kwargs)
# TO DO: Add in querysets of related tasks
context = context['related_tasks'] = task.objects.filter(related_project__id=project.id)
The problem seems to be that "project.id" does not actually access the "id" of the particular project object. How to I add the tasks that are linked to the particular project object being updated via the task's "related_project" attribute (which has as a foreign key the project object)?

You can't use project.id because project is the model, not the instance you are updating. Normally you would call your model Project and a model instance project. Calling your model project will confuse other users, as they'll assume it's an instance instead of a model.
In the get_context_data method, you can access the object with self.object.
context['related_tasks'] = task.objects.filter(related_project__id=self.object.id)
Make sure you don't set context = context['related_tasks'] = ....

Related

how to auto fill an IntegerField in django model based on a M2M relation?

I have a model named Post, and this model has a many to many relation with another model named LikeIps.
class Post(models.Model):
title = models.CharField(max_length=250)
summary = models.TextField()
content = RichTextField()
likes = models.ManyToManyField(LikeIP, related_name="liked_posts", blank=True)
like_count = models.IntegerField(editable=False)
def __str__(self):
return self.title
I want to fill likes_count field automatically base on the number of object in likes query set something like this:
like_count = obj.likes.count()
how should I do that?
You can use Django signals docs here Django Post-save signal
This will be called after the object is stored inside db, so you can make changes in the instance.
#receiver(post_save, sender=Post)
def save_post(sender, instance, created, **kwargs):
# or some other checks/logic
if created:
instance.like_count = obj.likes.count()
instance.save()
Update: m2m field in a django model is updated after the record is saved in db. So post_save signal could be used.

AttributeError when trying to created nested serializer in Django REST Framework

I have two models of sets, and cards. Theses models have one to many relationship where there are many cards in a single set. These model are join by a foreign key - each card has set_id which is equal to the id of the set. These IDs are UUID.
I am trying to create a serializer using Django REST Framework where I return the details of the set, as well as including all the cards that are part of the set.
error
Got AttributeError when attempting to get a value for field `cards` on serializer `SetSerializers`.
The serializer field might be named incorrectly and not match any attribute or key on the `Set` instance.
Original exception text was: 'Set' object has no attribute 'cards'.
serializers.py
class CardSerializers(serializers.ModelSerializer):
class Meta:
model = Card
fields = ['id', 'number', 'name', 'set_id']
class SetSerializers(serializers.ModelSerializer):
cards = CardSerializers()
class Meta:
model = Set
fields = ['id', 'code', 'name', 'releaseDate','cards']
models.py
class Set(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
...
objects = models.Manager()
def __str__(self):
return self.name
class Card(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
...
set = models.ForeignKey(Set, on_delete=models.CASCADE, related_name='Cards', related_query_name='Cards')
objects = models.Manager()
def __str__(self):
return self.name
views.py
class SetsIndividualData(ListAPIView):
serializer_class = SetSerializers
def get_queryset(self):
setCode = self.kwargs.get('setCode')
queryList = Set.objects.filter(code=setCode.upper())
return queryList
There are a few mistakes in your code.
models.py:
In the Card model, the related_name of the FK should be lowercase as per below:
set = models.ForeignKey(Set, related_name="cards", on_delete=models.CASCADE)
serializers.py:
In your SetSerializers you had the right idea, but you need to be more specific with your CardSerializers, as you are describing a "to-many" relationship.
Please refer to the docs here: https://www.django-rest-framework.org/api-guide/relations/#nested-relationships
Moreover, from your views.py I can see you will only send GET requests, therefore you can have your cards to be read only.
So the serializer card attribute would look like the below:
cards = CardSerializers(many=True, read_only=True)
views.py:
It looks like you want to retrieve a set by id.
ListAPIView is the wrong generics, you should use RetrieveAPIView instead, as it provides a get method and is used to retrieve a single model instance.
Please refer to the docs for more information: https://www.django-rest-framework.org/api-guide/generic-views/#retrieveapiview

Using the Django admin site for specific instances of a model

I am working on my first Django app, and was thinking of using a rather abstract database schema, like this:
class ListCategories(models.Model):
name = models.TextField(max_length=200)
type = models.TextField(max_length=200)
class ListItems(models.Model):
category = models.ForeignKey('ListCategories', on_delete=models.CASCADE)
item = models.TextField(max_length=200)
sorstorder = models.IntegerField()
class ObjectType(models.Model):
name = models.TextField(max_length=200)
class Object(models.Model):
type = models.ForeignKey('ObjectType', on_delete=models.CASCADE)
name = models.TextField(max_length=200)
class ObjectTypeProperties(models.Model):
name = models.TextField(max_length=200)
object_type = models.ForeignKey('ObjectType', on_delete=models.CASCADE)
list_category = models.ForeignKey('ListCategories', null=True, on_delete=models.CASCADE)
class ObjectProperties(models.Model):
object = models.ForeignKey('Object', on_delete=models.CASCADE)
property = models.ForeignKey('ObjectTypeProperties', on_delete=models.CASCADE)
list_item = models.ForeignKey('ListItems', on_delete=models.CASCADE)
result = models.TextField(max_length=200)
class HistoricalNumericalData(models.Model):
object = models.ForeignKey('Object', on_delete=models.CASCADE)
object_property = models.ForeignKey('ObjectProperties', on_delete=models.CASCADE)
value = models.FloatField()
class Image(models.Model):
object = models.OneToOneField('Object',on_delete=models.CASCADE)
image = models.ImageField()
def image_tag(self):
return mark_safe('<img src="{}"/>'.format(self.image.url))
image_tag.short_description = 'Image'
This is very flexible on the DB, as you can add object types and object properties by simply adding lines to the DB. However, I would like to use the admin interface to add new Objects to the database, and this is where this schema is tricky to use. The form would need to be different for each object type, however, as they would have not the same properties.
Is there a way to register models to use with the admin site that behave differently according to a field in a model? In my case, the Object.type field would dictate the nature of the form.
Would it be better to just define more concrete models?
You can try to use ModelAdmin.get_fieldsets() method, as you receive an object instance you can modify which fieldsets you want to publish, check the docs -> https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_fieldsets
Otherwise, U can explore to use ModelAdmin.get_form(), build custom forms for each Object.type and instantiate de proper one for each case, docs here -> https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_form
Hope this puts you on the right way.
G.

Displaying fields other than pk in a Django Form with a ModelChoiceField

I am building a website where users can upload files and can attach uploads to projects that they created beforehand. The upload is done with a django form where the user can specify the title, comments, etc... There is also a dropdown list where the user can choose from the existing projects that he created (list of projects is dependent on user)
As of now the dropdown only shows the (autogenerated) project id which is the pk of the model Project.
I want the dropdown to show the names of the projects and not the project ID which is not very meaningful for the user.
I have already tried
to_field_name='name'
but that didn't work
I have also tried
Project.objects.filter(user=user).values_list('name')
or
Project.objects.filter(user=user).values('name')
the last two options show the project name in {'projectname} but when I select them and submit the form the error "Select a valid choice. That choice is not one of the available choices."
This is my code:
models.py
class Upload(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
upload_date = models.DateTimeField(default=timezone.now)
comments = models.CharField(max_length=10000, null=True)
title = models.CharField(max_length=10000, null=True)
project = models.CharField(max_length=99, default='--None--')
forms.py
class UploadForm(ModelForm):
project = ModelChoiceField(label='Select Project', queryset=Project.objects.all(), to_field_name='name',
empty_label='--Select Project--')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(UploadForm, self).__init__(*args, **kwargs)
if user is not None:
self.fields['project'].queryset = Project.objects.filter(user=user)
class Meta:
model = Upload
fields = ['title', 'project', 'upload_date', 'comments']
According to docs
The str() method of the model will be called to generate string representations of the objects for use in the field’s choices. To provide customized representations, subclass ModelChoiceField and override label_from_instance. This method will receive a model object and should return a string suitable for representing it.
https://docs.djangoproject.com/en/2.2/ref/forms/fields/#modelchoicefield
so you should define __str__() method for Project model e.g.
def __str__(self):
return self.name

manytomany field not working django-cms

I am using django-cms
many to many field is working fine when i add plugins or update but
after publish page i didn't get any data where i add many to many field.
models are look like this
#python_2_unicode_compatible
class ClientLogo(CMSPlugin):
client_logo = models.ManyToManyField(LogoPluginModel, blank=True)
class LogoPluginModel(CMSPlugin):
title = models.CharField(max_length=100)
here field 'client_logo' will be disapper when i publish djagno-cms page
The relations need to be explicitly copied through the copy_relations method (see django-cms models manual). It's also advantageous to add a related_name property to the ManyToManyField, especially when a model contains multiple sets of foreign keys or ManyToManyFields.
#python_2_unicode_compatible
class ClientLogo(CMSPlugin):
client_logo = models.ManyToManyField(
LogoPluginModel,
blank=True,
related_name='client_logos',
)
class LogoPluginModel(CMSPlugin):
title = models.CharField(max_length=100)
def copy_relations(self, oldinstance):
self.client_logos.all().delete()
for logo in oldinstance.client_logos.all():
logo.pk = None
logo.showroom = self
logo.save()

Categories