django models storing request responses - python

I'm trying to save the details of responses from apis (typically undocumented).
The api's are likely to change over time and I will re request the data from them periodically which often is likely to have no change.
I therefore just want to save responses when they change and a time stamp when I last checked the response with the api.
I did start to model via the Django ORM, but I've most likely make it overly complicated trying to normalise the data.
Requests can be as usual, plain GET request, GET with query or POST with data.
Responses should really be unique to a url (scheme, hostname and path), and query or payload, but I'm not sure how to best model this. And I feel the response JSON or text has a many 2 one relationship with the response. However I feel there must be a simpler way as I really just started with not wanting to duplicate the storage of responses more than necessary as mentioned above.
class Request(models.Model):
scheme = models.CharField(max_length=10)
hostname = models.TextField()
path = models.TextField(null=True)
method = models.CharField(max_length=10)
headers = models.TextField()
class Meta:
unique_together = ('scheme', 'hostname', 'path', 'method')
def __str__(self):
return self.url
class RequestQuery(models.Model):
request = models.ForeignKey(Request)
query = models.TextField()
def __str__(self):
return self.query
class RequestPayload(models.Model):
request = models.ForeignKey(Request)
payload = models.TextField()
def __str__(self):
return self.payload
class Response(models.Model):
request = models.ForeignKey(Request)
query = models.ForeignKey(RequestQuery, null=True)
payload = models.ForeignKey(RequestPayload, null=True)
headers = models.TextField()
status_code = models.IntegerField()
json = JSONField(null=True) # stores in either json or text depending on if valid json
text = models.TextField(null=True)
class Meta:
unique_together = ('request', 'status_code')
def _str_(self):
return str(self.request.url) + str(self.status_code)
class ResponseHistory(models.Model):
time_stamp = models.DateTimeField(auto_now_add=True)
response = models.ForeignKey(Response)
def __str__(self):
return self.time_stamp

Related

annotate result not included in Json response

I want to count votes as per Model, and its working fine when i test this query in Django shell but when passing as a json i cannot see the column total_votes when i get Json Response.
class Model(models.Model):
title = models.CharField(max_length=255, null=False, blank=False)
description = models.TextField(null=False, blank=False)
class ModelVote(models.Model):
model = models.ForeignKey(Model, on_delete=models.CASCADE)
vote = models.BooleanField()
created_at = models.DateTimeField(auto_now_add=True)
from django.core.serializers import serialize
class ModelList(View):
def get(self, *args, **kwargs):
models = Model.objects.annotate(total_votes=Count('modelvote'))
return HttpResponse(serialize("json", models), content_type='application/json')
This is a known limitation of the serialization framework
http://code.djangoproject.com/ticket/5711
Below query solved my issue of total_votes not passing as Json Response. just adding .values() worked.
Model.objects.annotate(total_votes=Count('modelvote')).values()
return JsonResponse({'models': list(models)}, status=200)

How can i handle django nested models?

I have a User, Post and Tag model in Django. Tag model is not relevant for this topic. I can get all the data to the front end with nested objects. In the other hand when i want to create a new post i send the post data to django and in django view i am trying to update the data with relating the logged user to the "Post" but when i do that it gives me;
{'owner': {'username': [ErrorDetail(string='A user with that username already exists.', code='unique')]}}
error. How can i solve this error ?
models.py;
class Post(models.Model):
# Post specs
title = models.CharField(max_length=100, null=False)
place = models.CharField(max_length=100, null=False)
notes = models.CharField(max_length=10000, null=False)
tags = models.ManyToManyField(Tag)
start_date = models.DateField(null=True)
end_date = models.DateField(null=True)
created_at = models.DateField(auto_now=True)
owner = models.ForeignKey(User , null = True, on_delete=models.SET_NULL)
serializers.py;
class PostSerializer(serializers.ModelSerializer):
tags = serializers.SlugRelatedField(
many=True,
queryset=Tag.objects.all(),
slug_field='name'
)
owner = UserSerializer()
class Meta:
model = Post
fields = ('title','place','notes','start_date','end_date','created_at','id','owner','tags')
By the way if i change serializer.py like
owner = UserSerializer
it gives just primary key value. In front end i cant to anything with a integer number and i dont want to make an another api call for user model. Lastly view post function;
def post(self, request, format =None):
"""
Creates a post
"""
post = request.data ## copy dictionary to a variable
authenticatedUserDataAsDict = request.user.__class__.objects.filter(pk=request.user.id).values().first()
post.update( {'owner': authenticatedUserDataAsDict} ) ## attach authenticated user to post end
serializer = PostSerializer(data = post) ## serialize the dict
if serializer.is_valid():
serializer.save() ## if data valid save it.
return Response(serializer.data, status = status.HTTP_201_CREATED)
print("not valid->",serializer.errors)
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) # if it's not raise http 400
SOLVED
Hi again, it seems that rest framework have no idea about our request(create or get wise) because we are dealing with nested serializers.
So i found this article in medium and it helped me to solve my problem.

django rest fileupload with link returned

I was searching through stackoverflow for example of working fileupload APIView (using DRF of latest versions), I've already tried with many different code samples but none worked (some of them are deprecated, some - isn't what i want)
I have these models:
class Attachment(models.Model):
type = models.CharField(max_length=15, null=False)
attachment_id = models.CharField(max_length=50, primary_key=True)
doc = models.FileField(upload_to="docs/", blank=True)
I don't wanna use forms and anything else but rest parsers
I want to get POST'ed fields (for example name) in future
I believe the solution is easy but this doesnt work
class FileUploadView(APIView):
parser_classes = (FileUploadParser,)
def post(self, request):
file_obj = request.FILES
doc = Attachment.objects.create(type="doc", attachment_id=time.time())
doc.doc = file_obj
doc.save()
return Response({'file_id': doc.attachment_id}, status=204)
removing parser_class will solve almost all problems here. Try the following snippet
class FileUploadView(APIView):
def post(self, request):
file = request.FILES['filename']
attachment = Attachment.objects.create(type="doc", attachment_id=time.time(), doc=file)
return Response({'file_id': attachment.attachment_id}, status=204)
Screenshot of POSTMAN console

What is the proper way to structure models.py?

I'm trying to build the right models for my Django app. I'm trying to build something that will allow a user to save a URL into one (or more) playlist(s) that is tied to that user. Before I implement this, I want to make sure that this is the best way to structure my models.py.
class UserProfile(models.Model):
user = models.ForeignKey(User, primary_key=True) #what is the difference between ForeignKey and OneToOne? Which one should I use?
Playlist = models.CharField('Playlist', max_length = 2000) #1 user should be able to have multiple playlists and the default playlist should be "Favorites"
def __unicode__(self):
return self.User
class Videos(models.Model):
Video_url = models.URLField('Link to video', max_length = 200, null=True, blank=True)
Playlist = models.ManyToManyField(Playlist) #this should connect to the playlists a user has. A user should be able to save any video to any plalist, so perhaps this should be ManyToMany?
def __unicode__(self):
return self.Video_url
Woah. Firstly the question is probably too "localised" for SO. Anyway. I'd do it like this:
class PlayList(models.Model):
playlist = models.CharField(max_length=2000)
class UserProfile(models.Model):
# do you want each `User` to only have one `UserProfile`? If so then OneToOne
# primary keys are automatically formed by django
# how django handles profiles: https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
user = models.ForeignKey(User)
def __unicode__(self):
return self.User
class UserPlayList(models.Model):
# don't capitalise attributes, if you haven't seen PEP8 before, do now: http://www.python.org/dev/peps/pep-0008/
profile = models.ForeignKey(User)
playlist = models.ForeignKey(PlayList)
class Video(models.Model):
video_url = models.URLField(max_length=200, null=True, blank=True, help_text="Link to video")
def __unicode__(self):
return self.video_url
class VideoPlayList(models.Model):
video = models.ForeignKey(Video)
play_list = models.ForeignKey(UserPlayList)

Tastypie - Nested Resource field not found

I have this code:
#api model
class VideoResource(ModelResource):
class Meta:
queryset = Video.objects.all()
include_resource_uri = False
resource_name = 'video'
authorization = DjangoAuthorization()
class QuestionResource(ModelResource):
user = fields.ToOneField(UserResource,'user',full=True)
video = fields.ForeignKey(VideoResource,'video',full=True)
class Meta:
queryset = Question.objects.all()
resource_name = 'question'
include_resource_uri = False
authorization = DjangoAuthorization()
def obj_create(self, bundle, request=None, **kwargs):
import json
temp = json.loads(request.body, object_hook=_decode_dict)
video = Video.objects.get(pk=temp['video'])
return super(QuestionResource, self).obj_create(bundle, request, user=request.user, video=video)
#model
class Question(models.Model):
text = models.CharField('Question',max_length=120)
created = models.DateTimeField(auto_now_add=True)
enabled = models.BooleanField(default=True)
flag = models.BooleanField(default=False)
allow_comments = models.BooleanField(default=True)
thumbnail_url = models.CharField(default='video.jpg',blank=True, null=True,max_length=200)
user = models.ForeignKey(User)
video = models.ForeignKey(Video)
def __unicode__(self):
return self.text;
class Video(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now_add=True)
url = models.URLField(default="")
user = models.ForeignKey(User)
def __unicode__(self):
return str(self.pk) + ' > ' + self.status
The problem is that I am getting this error when sending this object:
{"video":21,"text":"sadasds"}
The 'video' field has was given data that was not a URI, not a
dictionary-alike and does not have a 'pk' attribute: 21.
If I comment this line:
video = fields.ForeignKey(VideoResource,'video',full=True)
Everything works fine, but then I cannot get this information (video)
when asking to /api/v1/questions/
My question is:
Should I create to resources, one to post and another to retrieve
information <- this seems not a really good solution.
or
How can I create Nested Resources ? I tried to follow the example on
the web http://django-tastypie.readthedocs.org/en/latest/cookbook.html#nested-resources
but as you can see for some reason is not working.
maybe your eyes can help me find the error :)
Thanks!
The 'video' field has was given data that was not a URI, not a dictionary-alike and does not have a 'pk' attribute: 21.
So, this means that the integer 21 does't meet the requirements for that field, it also give a vague hint of what will meet the requirements.
first, you can send in the URI for the record, this is probably the most correct way as URIs are really unique while pk's are not.
{"video":"/api/v1/video/21","text":"sadasds"}
or, you can send in an dictionary-alike object with the pk field set.
{"video":{'pk':21},"text":"sadasds"}
The reason it works when you comment out the ForeignKey field is because then tastypie treats it as a IntegerField, which can be referenced by a plain integer.
This had me stunted for a while to, hope it helps!

Categories