I am trying to use django rest framework to create instances of a Bookmark model for different types of content, for example Book.
The code:
models.py
class Bookmark(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
api.py
from django.contrib.contenttypes.models import ContentType
from rest_framework import viewsets, mixins, serializers, permissions
from .models import Bookmark
class BookmarkSerializer(serializers.ModelSerializer):
class Meta:
model = Bookmark
fields = ('id', 'user', 'content_type', 'object_id')
class BookmarkViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = Bookmark.objects.all()
serializer_class = BookmarkSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
content_type = self.request.query_params.get('content_type')
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
object_id = self.request.query_params.get('object_id')
serializer.save(user=self.request.user, content_type=ct, object_id=object_id)
def get_queryset(self):
items = Bookmark.objects.filter(user=self.request.user)
content_type = self.request.query_params.get('content_type', None)
if content_type:
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
items = items.filter(content_type=ct)
object_id = self.request.query_params.get('object_id', None)
if object_id:
items = items.filter(object_id=object_id)
return items
get_queryset part is fine. But perform_create fails when I try to create a new Bookmark:
var item = new Bookmark({'content_type': 'books.book', 'object_id': self.book_id});
item.save();
The response:
{"user":["This field is required."],"content_type":["Incorrect type. Expected pk value, received unicode."]}
It is not clear to me how I am supposed to do this. I would appreciate any feedback.
You should post your model definition as well, but the error you are getting is saying that you also need to pass a user argument to Bookmark and that you should be passing a pk value (aka an integer that is the id of the object you're pointing to) for content_type.
I can't say anything about the error message concerning the user, but here is my 2 cents about the second one.
You do not simply give a path to your model as a string to the content_type key.
Apparently
, you have to use the get_for_model method which is passed as parameter a model (The model 'Book', not an instance of it), and returns an object of type ContentType. This object's id is what you should pass as a value.
Both previous answers pointed me in the right direction. In the end, the solution was not to override perform_create, but create function:
def create(self, request, *args, **kwargs):
content_type = request.data['content_type']
if content_type:
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
request.data['user'] = request.user.id
request.data['content_type'] = ct.id
#object_id already passed as id in request.data
A further explanation can be read here:
https://github.com/encode/django-rest-framework/issues/3470
Related
So I made a django ajax that everytime there is a new message the notification object that I add to my user models will be +1, the problem is that I dont know why is giving me this error:
django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.
here is my code:
views.py:
def count_notification(request):
user = request.user
obj = user.profile.objects.get(pk=1)
obj.notifications = obj.notifications +1
obj.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
urls.py:
path('messages/notification/', count_notification)
html file js
// Add the notification val
$.get('notification/')
models.py:
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
to=settings.AUTH_USER_MODEL,
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.FloatField(default='0')
thank you for the help
obj = user.profile.objects.get(pk=1)
i think your app is dealing with many users, each user has its own profile with its own notification count, so why you are fixing the id / pk to 1, you need to get the related profile, not hard code the id
try this code below
def count_notification(request):
user = request.user
user.profile.notifications += 1
user.profile.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
and change
notifications = models.FloatField(default='0')
to
notifications = models.IntegerField(default='0')
it does make more sens since notifications count is an integer not float nand don't forget to rerun migrations
Update
i guess you have not defined a custom User model in settings.py in this case you need to make some changes in models.py
refer to https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#reusable-apps-and-auth-user-model
from django.contrib.auth import get_user_model # get the default User model
User = get_user_model()
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
# to=settings.AUTH_USER_MODEL, # you don't need this since you didn't
# defined a custom User model in
# setting.py
to = User, # HERE
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.IntegerField(default='0') # change the field type
Update 2
from django.contrib.auth import get_user_model
User = get_user_model()
..
def count_notification(request):
# user = request.user
user = User.objects.filter(id=request.user.id).first() # HERE
user.profile.notifications += 1
user.profile.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
I have just started Django and what I was trying to implement $http.post method of angular to post form data to my Django database,and my further plan was to update the view to display the results without page refresh.so what i thought to post data in django db and then my view function will return a jsonresponse,of that posted data which i can use to update the view using $http.get method.
But the problem is occuring with me is whenever i post data it is not able to post the data and returning an empty json response.
Here is my codes which i am working on:-
urls.py
from django.conf.urls import url
from . import views
app_name='demo'
urlpatterns=[
url(r'^$',views.index,name="index"),
url(r'^add_card/$',views.add_card,name="add_card")
]
views.py
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from .forms import CardForm
from .models import Card
# Create your views here.
def add_card(request):
saved = False
if request.method == 'POST':
#print('hi')
form = CardForm(request.POST)
#print('hi')
if form.is_valid():
#print('hi')
card = Card()
#print('hi')
card.content = form.cleaned_data['content']
#print('hi')
saved = True
card.save()
#print('hi')
return JsonResponse({'body':list(q.content for q in Card.objects.order_by('-id')[:15])})
else:
return HttpResponse(
json.dumps({"nothing to see": "this isn't happening"}),
content_type="application/json"
)
def index(request):
return render(request,'demo/index.html',{'form':CardForm()})
controller.js
var nameSpace = angular.module("ajax", ['ngCookies']);
nameSpace.controller("MyFormCtrl", ['$scope', '$http', '$cookies',
function ($scope, $http, $cookies) {
$http.defaults.headers.post['Content-Type'] = 'application/json';
// To send the csrf code.
$http.defaults.headers.post['X-CSRFToken'] = $cookies.get('csrftoken');
// This function is called when the form is submitted.
$scope.submit = function ($event) {
// Prevent page reload.
$event.preventDefault();
// Send the data.
var in_data = jQuery.param({'content': $scope.card.content,'csrfmiddlewaretoken': $cookies.csrftoken});
$http.post('add_card/', in_data)
.then(function(json) {
// Reset the form in case of success.
console.log(json.data);
$scope.card = angular.copy({});
});
}
}]);
My models.py:-
from django.db import models
# Create your models here.
class Card(models.Model):
content = models.TextField()
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.content
My forms.py-
from django import forms
from .models import Card
class CardForm(forms.ModelForm):
class Meta:
model = Card
fields = ['content']
You have some problems in your view.py code.
You need to create a new object in your database from the request data
You need to return the new data as a response
if form.is_valid():
new_content = form.cleaned_data['content']
card = Card.objects.create(content=new_content)
return JsonResponse(
list(Card.objects.all().order_by('-id').values('content')[:15]),
safe=False
)
That should return a list of the content value of your first 15 objects in your Card table after creating a new object in that table by the content provided, IF your form is valid.
Also your CardForm should be defined as follows:
class CardForm(forms.ModelForm):
class Meta:
model = Card
fields = ('content',)
Finally, your $http.post call is asynchronous, which means that when the .then is reached, there is a probability (almost a certainty) that the post request has not been resolved yet, thus your json.data is empty. To solve this problem:
$http.post('add_card/', in_data)
.then((json) => {
// Reset the form in case of success.
console.log(json.data);
$scope.card = angular.copy({});
});
A far better read and solutions on the asynchronous-to-synchronous calls are: ES6 Promises - Calling synchronous functions within promise chain, How do you synchronously resolve a chain of es6 promises? and Synchronous or Sequential fetch in Service Worker
Good luck :)
It's a part of project in django framework.
In a javascript file I'm writing a code segment to .get() a URL
e.g. /delete/student/<id_of_student> .
.
.
$.get($(this).attr('href'), location.reload(true), function(data){ alert("Put in bin !!"); });
.
.
When it will be called , from urls.py it will be mapped to a function which will put the row in trash_bin and from page the row will be deleted after reload.
But in runserver console it's throwing error like:
concrete_model = model._meta.concrete_model
AttributeError: type object 'builtin_function_or_method' has no attribute '_meta'
But in console.log showing correct url, say /delete/student/123.
Here's the trash part from models.py :
class TrashAction(models.Model):
user = models.ForeignKey(User)
timestamp = models.DateTimeField()
def __unicode__(self):
return "%d items by %s on %s" % (self.trashitem_set.count(), self.user, self.timestamp.strftime('%Y-%m-%d %H:%M'))
class TrashItem(models.Model):
json_data = models.TextField()
contenttype = models.ForeignKey(ContentType)
action = models.ForeignKey(TrashAction)
def __unicode__(self):
data = json.loads(self.json_data)[0]
return "%s[%d]" % (data['model'], data['pk'])
Here's view of trash.py :
def move_to_trash(request, obj):
"""
Moves an object to the trash bin.
"""
c = Collector(using='default')
c.collect([obj])
action = None
with transaction.atomic():
action = TrashAction()
action.user = request.user
action.timestamp = datetime.now()
action.save()
for i in c.instances_with_model():
item = TrashItem()
item.action = action
item.contenttype = ContentType.objects.get_for_model(i[1])
item.json_data = serializers.serialize('json', [ i[1] ])
item.save()
obj.delete()
def delete_product(request, student_id):
move_to_trash(request, id)
Could anybody please help me to understand why this error is coming?
I have a Contract class where contract_mod allows to extend a contract from a previous one. contract_mod should only show contracts related with the person that we selected previously.
The Contract class returns the field person. As I've never work with AJAX/jQuery I don't know where to start.
So, my goal is that the field contract_mod depends on hte field person (using the Admin interface).
class Contract(models.Model):
person = models.ForeignKey(Person) #person hired
contract_mod = models.OneToOneField('self', blank = True, null = True) #allows to extend a contract
...
As the field contract_mod is OneToOneField I can't use django-smart-selects or django-ajax-select
In a similar situation I did the following (now adapted to your model):
models
class Person(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
def get_name(self):
return self.name
class Contract(models.Model):
person = models.ForeignKey(Person) #person hired
contract_mod = models.OneToOneField('self', blank = True, null = True)
contract_name = models.CharField(max_length=20) #just for testing
def __unicode__(self):
return self.get_name() + " " +self.contract_name
def get_name(self):
return self.person.get_name() #to make sure you get the person name in the admin
def contract_mod_name(self):
if self.contract_mod:
return self.contract_mod.contract_name
else:
return ""
admin
class SelectField(forms.ChoiceField):
def clean(self, value):
return value
class CForm(forms.ModelForm):
contracts_from_selected = SelectField()
class Meta:
model = Contract
widgets = { 'contract_mod' : forms.widgets.Select(attrs={'hidden' : 'true'}) }
class CAdmin(admin.ModelAdmin):
form = CForm
list_display = ('contract_name','get_name','contract_mod_name')#what you like
def save_model(self, request, obj, form, change):
if request.POST.get('contracts_from_selected'):
obj.contract_mod=Contract.objects.get(id=int(request.POST.get('contracts_from_selected')))
obj.save()
Override the change_form.html template (by copying it from the django/contrib/admin/templates/admin into your yourapp/templates/admin/yourapp directory) and add the following Javascript:
$(function () {
$("#id_person").change(function () {
var options = $("#id_contract_mod option").filter(function () {
return $(this).html().split(" ")[0] === $("#id_person option:selected").html();
}).clone();
$("#id_contracts_from_selected").empty();
$("#id_contracts_from_selected").append(options);
});
});
One shortcoming is, that this uses the visible html entry to store the person - contract relation. So the person is also visible in the dropdown. To avoid this you could add an attribute to the options instead - see here:
Django form field choices, adding an attribute
Yes and it would be good to completely hide the contract_mod ChoiceField. Via the hidden=True in the widget only the dropdown is hidden.
I am having a hard time getting access to the rawCorpus variable that I return in my views. When ever I test it with an alert: alert({{ rawCorpus }}) it pops up with undefined as the message. I really need a way to test things as I go along in views. IE: a print method for my individual variables that doesn't have to go through the return process.
views.py
def homesite(request):
rawCorpus = ""
corpusitems = CorpusItem.objects.order_by('name')
if (request.method == 'POST') and "namefortextinput" in request.POST:
f = CorpusItemForm(request.POST)
if f.is_valid():
new_corpusitem = f.save()
elif (request.method == 'POST') and 'selectedCorpusChoiceTokenizeWords' in request.POST:
g = CorpusChoiceForm(request.POST)
if g.is_valid():
new_corpuschoice = g.save()
rawCorpus = g.__getitem__(name)
Models.py
class CorpusItem(models.Model):
name = models.CharField(max_length=100)
content = models.CharField(max_length=9000)
def __unicode__(self):
return self.name
class CorpusItemForm(ModelForm):
class Meta:
model = CorpusItem
def __str__():
return name
from django import forms
class CorpusChoice(models.Model):
name = models.CharField(max_length=100)
class CorpusChoiceForm(ModelForm):
class Meta:
model = CorpusChoice