Django: 'Country' object has no attribute 'all' - python

Whenever I try to access my model "Youtube" from django admin, it shows me this error. I am sensing I have messed it up in the YoutubeAdmin, inside the getCountry method. How can I solve this? Cannot find a way. It says model Youtube doesn't have a filed getCountry in the error.
My model :
class Youtube(models.Model):
link = models.CharField(max_length=2000)
scrap_interval_hour = models.IntegerField(default=8)
last_scrapped_datetime = models.DateTimeField(blank=True, null=True)
is_feed = models.BooleanField(default=False)
created_date = models.DateField(
auto_now_add=True)
# Foriegn Key
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class Meta:
ordering = ('created_date', )
admin
from django.contrib import admin
from fpn.models import Youtube
from datetime import timedelta
class YoutubeAdmin(admin.ModelAdmin):
def get_fields(self, request, youtube=None):
fields = super(YoutubeAdmin, self).get_fields(request, youtube)
fields.remove('last_scrapped_datetime')
if youtube:
fields.remove('country')
return fields
model = Youtube
list_per_page = 10
list_display = ('link', 'is_feed',
'getCountry', 'last_scrapped_datetime')
list_filter = ('country', 'is_feed', )
def getCountry(self, obj):
return "\n".join([coun.name for coun in obj.country.all()])

ForeignKey create many-to-one relation. This mean for one country you can have many Youtube objects. But not reverse, one Youtube object can have only one country.
So in your case you cannot query list of countries from youtube object. Since it has only one related country which you can access directly:
def getCountry(self, obj):
return obj.country.name

Related

Django - Sending post request using a nested serializer with many to many relationship. Getting a [400 error code] "This field may not be null] error"

I'm fairly new to Django and I'm trying to make a POST request with nested objects. This is the data that I'm sending:
{
"id":null,
"deleted":false,
"publishedOn":2022-11-28,
"decoratedThumbnail":"https://t3.ftcdn.net/jpg/02/48/42/64/360_F_248426448_NVKLywWqArG2ADUxDq6QprtIzsF82dMF.jpg",
"rawThumbnail":"https://t3.ftcdn.net/jpg/02/48/42/64/360_F_248426448_NVKLywWqArG2ADUxDq6QprtIzsF82dMF.jpg",
"videoUrl":"https://www.youtube.com/watch?v=jNQXAC9IVRw",
"title":"Video with tags",
"duration":120,
"visibility":1,
"tags":[
{
"id":null,
"videoId":null,
"videoTagId":42
}
]
}
Here's a brief diagram of the relationship of these objects on the database
I want to create a video and pass in an array of nested data so that I can create multiple tags that can be associated to a video in a many to many relationship. Because of that, the 'id' field of the video will be null and the 'videoId' inside of the tag object will also be null when the data is being sent. However I keep getting a 400 (Bad request) error saying {tags: [{videoId: [This field may not be null.]}]}
I'm trying to override the create method inside VideoManageSerializer so that I can extract the tags and after creating the video I can use that video to create those tags. I don't think I'm even getting to the create method part inside VideoManageSerializer as the video is not created on the database. I've been stuck on this issue for a few days. If anybody could point me in the right direction I would really appreciate it.
I'm using the following serializers:
class VideoManageSerializer(serializers.ModelSerializer):
tags = VideoVideoTagSerializer(many=True)
class Meta:
model = Video
fields = ('__all__')
# POST
def create(self, validated_data):
tags = validated_data.pop('tags')
video_instance = Video.objects.create(**validated_data)
for tag in tags:
VideoVideoTag.objects.create(video=video_instance, **tag)
return video_instance
class VideoVideoTagSerializer(serializers.ModelSerializer):
class Meta:
model = VideoVideoTag
fields = ('__all__')
This is the view which uses VideoManageSerializer
class VideoManageViewSet(GenericViewSet, # generic view functionality
CreateModelMixin, # handles POSTs
RetrieveModelMixin, # handles GETs
UpdateModelMixin, # handles PUTs and PATCHes
ListModelMixin):
serializer_class = VideoManageSerializer
queryset = Video.objects.all()
These are all the models that I'm using:
class Video(models.Model):
decoratedThumbnail = models.CharField(max_length=500, blank=True, null=True)
rawThumbnail = models.CharField(max_length=500, blank=True, null=True)
videoUrl = models.CharField(max_length=500, blank=True, null=True)
title = models.CharField(max_length=255, blank=True, null=True)
duration = models.PositiveIntegerField()
visibility = models.ForeignKey(VisibilityType, models.DO_NOTHING, related_name='visibility')
publishedOn = models.DateField()
deleted = models.BooleanField(default=0)
class Meta:
managed = True
db_table = 'video'
class VideoTag(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
deleted = models.BooleanField(default=0)
class Meta:
managed = True
db_table = 'video_tag'
class VideoVideoTag(models.Model):
videoId = models.ForeignKey(Video, models.DO_NOTHING, related_name='tags', db_column='videoId')
videoTagId = models.ForeignKey(VideoTag, models.DO_NOTHING, related_name='video_tag', db_column='videoTagId')
class Meta:
managed = True
db_table = 'video_video_tag'
I would consider changing the serializer as below,
class VideoManageSerializer(serializers.ModelSerializer):
video_tag_id = serializers.PrimaryKeyRelatedField(
many=True,
queryset=VideoTag.objects.all(),
write_only=True,
)
tags = VideoVideoTagSerializer(many=True, read_only=True)
class Meta:
model = Video
fields = "__all__"
# POST
def create(self, validated_data):
tags = validated_data.pop("video_tag_id")
video_instance = Video.objects.create(**validated_data)
for tag in tags:
VideoVideoTag.objects.create(videoId=video_instance, videoTagId=tag)
return video_instance
Things that have changed -
Added a new write_only field named video_tag_id that supposed to accept "list of PKs of VideoTag".
Changed the tags field to read_only so that it won't take part in the validation process, but you'll get the "nested serialized output".
Changed create(...) method to cooperate with the new changes.
The POST payload has been changed as below (note that tags has been removed and video_tag_id has been introduced)
{
"deleted":false,
"publishedOn":"2022-11-28",
"decoratedThumbnail":"https://t3.ftcdn.net/jpg/02/48/42/64/360_F_248426448_NVKLywWqArG2ADUxDq6QprtIzsF82dMF.jpg",
"rawThumbnail":"https://t3.ftcdn.net/jpg/02/48/42/64/360_F_248426448_NVKLywWqArG2ADUxDq6QprtIzsF82dMF.jpg",
"videoUrl":"https://www.youtube.com/watch?v=jNQXAC9IVRw",
"title":"Video with tags",
"duration":120,
"visibility":1,
"video_tag_id":[1,2,3]
}
Refs
DRF: Simple foreign key assignment with nested serializer?
DRF - write_only
DRF - read_only

HyperlinkedRelatedFIeld and OneToOne relationship - object has no attribute

For my app I have offices and HR users (users with an OneToOneField to HRProfile), and I want to be able to assign HRs to offices. The issue I'm facing is that I just can't access the User's 'email' field when trying to look it up through a HyperlinkedRelatedField on an OfficeSerializer.
Relevant models:
class User(AbstractBaseUser, PermissionsMixin):
...
email = models.EmailField(unique=True)
...
from polymorphic.models import PolymorphicModel
class Profile(PolymorphicModel):
...
related_user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name="profile",
)
...
class HRProfile(Profile):
some_hr_specific_field = models.CharField()
def __str__(self) -> str:
return self.related_user.email
class Office(models.Model):
...
assigned_hrs = models.ManyToManyField(
"users.HRProfile", related_name="offices", blank=True
)
...
View:
class UserViewSet(
GenericViewSet,
):
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = "email"
lookup_url_kwarg = "email"
lookup_value_regex = "[\\w#.]+"
...
# the OfficeViewSet just has the two required fields (queryset and serializer)
And the OfficeSerializer I'm having trouble with:
class OfficeSerializer(serializers.HyperlinkedModelSerializer):
...
assigned_hrs = serializers.HyperlinkedRelatedField(
queryset=HRProfile.objects.all(),
view_name="api:user-detail",
lookup_field="related_user.email",
many=True,
)
...
The above raises 'HRProfile' object has no attribute 'related_user.email' which I don't know what to make of, since the HRProfile.__str__ has no issues resolving the self.related_user.email path correctly for a given user.
I tried it with source='assigned_hrs.related_user', lookup_field='email' but to no avail. Also, I tried replacing the HyperlinkedRelatedField with an SlugRelatedField just to test, and it's the same issue with the slug_field="related_user.email".
I also tried using the double underscores instead of a period. What goes through however is lookup_field='some_hr_specific_field' which is surely not what I want, but it does resolve.
So I'm really running out of ideas, and any hints would be greatly appreciated!
Solved!
Define get_hrs on Office model:
class Office(models.Model):
def get_hrs(self):
hrs = self.assigned_hrs.all()
return [hr.related_user for hr in hrs]
And simply use the method as source:
class OfficeSerializer(serializers.HyperlinkedModelSerializer):
assigned_hrs = serializers.HyperlinkedRelatedField(
many=True,
queryset=User.objects.filter(groups__name="human_resource"),
source="get_hrs",
lookup_field="email",
view_name="api:user-detail",
)
# slugrelated works too :)
# assigned_hrs = serializers.SlugRelatedField(
# many=True,
# queryset=User.objects.filter(groups__name="human_resource"),
# source="get_hrs",
# slug_field="email",
# )

Django REST framework - filtering against query param with date Outside Views.py file

I created my "API" using REST framework, now trying to do filtering for it. That's how my models.py look for BookingStatement model.
class BookingStatement(BaseModel):
ticket_number = models.PositiveIntegerField(unique=True)
booking = models.OneToOneField(Booking, on_delete=models.PROTECT)
user_rate = AmountField()
agent_rate = AmountField()
total_amount = AmountField()
class Meta:
default_permissions = ()
def __str__(self):
return str(self.id)
Booking is One to One Key so the booking model has following Attributes.
class Booking(BaseModel):
bus_seat = models.ManyToManyField(Seat)
schedule = models.ForeignKey(Schedule, on_delete=models.PROTECT)
boarding_point = models.ForeignKey(
BoardingPoint,
on_delete=models.PROTECT,
null=True
)
remarks = models.JSONField(null=True, blank=True)
contact = PhoneNumberField(null=True)
booking_date_time = models.DateTimeField(auto_now_add=True)
class Meta:
default_permissions = ()
verbose_name = 'Booking'
verbose_name_plural = 'Bookings'
def __str__(self):
return '{}-{}'.format(self.user, self.customer_name)
I used generic ListAPIView in my views.py as following.
class BusCompanyTicketDetailView(generics.ListAPIView, BusCompanyMixin):
serializer_class = serializers.TicketDetailResponseSerializer
def get_queryset(self):
travel_date = (int(self.request.query_params.get('booking_date')))
print(booking_date)
return usecases.ListBusCompanyTicketUseCase(date=#'what should i pass?'#).execute()
I use usecases.py to filter booking_date_time with url as following.
http://127.0.0.1:8000/api/v1/ticket/list?booking_date=2021-1-29
So my usecase file to filter the Booking time is as following.
class ListBusCompanyTicketUseCase(BaseUseCase):
def __init__(self, date:datetime):
self._date = datetime
def execute(self):
self._factory()
return self._booking_statements
def _factory(self):
self._booking_statements = BookingStatement.objects.filter(booking__booking_date_time=?? need to get date from views.)
Problem is I don't know to how to get query params from url in my usecases to filter with booking date any help will be very helpful.
you should use django-filter to implement filtering on the viewsets. It's a bit of knowledge you have to build up, but once you understand it, you can do a lot of complex filtering logic with it. Trying to implement a filtering system yourself is always more difficult in the long run. For starting point, check out the official documentation: https://www.django-rest-framework.org/api-guide/filtering/. For the filter, check out the documentation on DRF integration: https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html.

Django Rest Framework - perform Put request by passing a JSON object instead of just ID

I'm working on a Project using Python(3), Django(1.11) and DRF(3.6) in which I have to perform a PUT request by passing a nested nested instead of an ID.
Here's What I have tried:
models.py:
class Actor(models.Model):
id = models.CharField(primary_key=True, max_length=255)
login = models.CharField(max_length=255)
avatar_url = models.URLField(max_length=500)
class Repo(models.Model):
id = models.CharField(primary_key=True, max_length=255)
name = models.CharField(max_length=255)
url = models.URLField(max_length=500)
class Event(models.Model):
id = models.CharField(primary_key=True, max_length=255)
type = models.CharField(max_length=255)
actor = models.ForeignKey(Actor, related_name='actor')
repo = models.ForeignKey(Repo, related_name='repo')
created_at = models.DateTimeField()
serializers.py:
class ActorSerializer(serializers.ModelSerializer):
class Meta:
model = Actor
fields = ('id', 'login', 'avatar_url')
class RepoSerializer(serializers.ModelSerializer):
class Meta:
model = Repo
fields = ('id', 'name', 'url')
class EventModelSerializer(serializers.ModelSerializer):
actor = ActorSerializer(many=False)
repo = RepoSerializer(many=False)
class Meta:
model = Event
fields = ('id', 'type', 'actor', 'repo', 'created_at')
depth = 1
def create(self, validated_data):
return Event.objects.create(**validated_data)
Update: Here when I submit a post request with the following object:
{
"id":ID,
"type":"PushEvent",
"actor":{
"id":ID,
"login":"daniel33",
"avatar_url":"https://avatars.com/2790311"
},
"repo":{
"id":ID,
"name":"johnbolton/exercitationem",
"url":"https://github.com/johnbolton/exercitationem"
},
"created_at":"2015-10-03 06:13:31"
}
it return this error as: TypeError: 'ValueError: Cannot assign "OrderedDict([('id', '2790311'), ('login', 'daniel33'), ('avatar_url', 'https://avatars.com/2790311')])": "Event.actor" must be a "Actor" instance.
views.py:
class Actor(generics.GenericAPIView):
serializer_class = EventModelSerializer
queryset = EventModel.objects.all()
def update(self):
actor = EventModel.objects.filter(actor_id=self.request.data('id'))
print(actor)
return HttpResponse(actor)
Sample Input Object:
{
"id":3648056,
"login":"ysims",
"avatar_url":"https://avatars.com/modified2"
}
The requirements is:
Updating the avatar URL of the actor: The service should be able to update the avatar URL of the actor by the PUT request at /actors. The actor JSON is sent in the request body. If the actor with the id does not exist then the response code should be 404, or if there are other fields being updated for the actor then the HTTP response code should be 400, otherwise, the response code should be 200.**
I'm little bit confused about how to perform the PUT request without
passing an ID?
I have seen your two-three questions asked today. I think You are asking the wrong question. I think what you need is three models Event, actor and repo. the event model has two foreign key fields as actor and repo. Now what you want it to update the actor models avtar_url field. OK?
class Actor(models.Model):
avtar_url = models.CharField(max_length=255)
# Other Fields
class Repo(models.Model):
# Other Fields
class EventModel(models.Model):
id = models.CharField(primary_key=True, max_length=255)
type = models.CharField(max_length=255)
actor = models.ForaignKey(Actor)
repo = models.ForaignKey(Actor)
created_at = models.DateTimeField()
Now for create and update the NESTED EventModel entry use writable-nested-serializers. By this, you can directly update the avatar_url for Actor by its id.
UPDATE as per Request
you need to change your create method as following so that it creates Actor, Repo and link their ids to Event
def create(self, validated_data):
actor = validated_data.pop('actor')
repo = validated_data.pop('repo')
actor = Actor.objects.create(**actor)
repo = Repo.objects.create(**repo)
return Event.objects.create(actor=actor,repo=repo,**validated_data)

Searching by related fields in django admin

I've been looking at the docs for search_fields in django admin in the attempt to allow searching of related fields.
So, here are some of my models.
# models.py
class Team(models.Model):
name = models.CharField(max_length=255)
class AgeGroup(models.Model):
group = models.CharField(max_length=255)
class Runner(models.Model):
"""
Model for the runner holding a course record.
"""
name = models.CharField(max_length=100)
agegroup = models.ForeignKey(AgeGroup)
team = models.ForeignKey(Team, blank=True, null=True)
class Result(models.Model):
"""
Model for the results of records.
"""
runner = models.ForeignKey(Runner)
year = models.IntegerField(_("Year"))
time = models.CharField(_("Time"), max_length=8)
class YearRecord(models.Model):
"""
Model for storing the course records of a year.
"""
result = models.ForeignKey(Result)
year = models.IntegerField()
What I'd like is for the YearRecord admin to be able to search for the team which a runner belongs to. However as soon as I attempt to add the Runner FK relationship to the search fields I get an error on searches; TypeError: Related Field got invalid lookup: icontains
So, here is the admin setup where I'd like to be able to search through the relationships. I'm sure this matches the docs, but am I misunderstanding something here? Can this be resolved & the result__runner be extended to the team field of the Runner model?
# admin.py
class YearRecordAdmin(admin.ModelAdmin):
model = YearRecord
list_display = ('result', 'get_agegroup', 'get_team', 'year')
search_fields = ['result__runner', 'year']
def get_team(self, obj):
return obj.result.runner.team
get_team.short_description = _("Team")
def get_agegroup(self, obj):
return obj.result.runner.agegroup
get_agegroup.short_description = _("Age group")
The documentation reads:
These fields should be some kind of text field, such as CharField or TextField.
so you should use 'result__runner__team__name'.

Categories