This should be a easy one. I am getting an error that I don't understand. I think I need to prefill the DB with null values to fix this but I am unclear if that is the case. I am getting the following error which I don't understand the meaning of...
Cannot add "": instance is on database "None", value is on database "default"
the models.py has the following definitions...
class Tag(models.Model):
name = models.CharField(max_length = "20", primary_key = True)
class Page(models.Model):
name = models.CharField(max_length="45", primary_key = True)
content = models.TextField(blank=True)
pub_date = models.DateTimeField('date published')
tags = models.ManyToManyField(Tag)
def __unicode__(self):
return self.name
the view that is failing to render is ...
def save_page(request, page_name):
date = datetime.date.today()
content = request.POST["content"]
tag_list = []
if "tags" in request.POST:
tags = request.POST["tags"]
tag_list = [Tag.objects.get_or_create(name=tag)[0] for tag in tags.split()]
try:
page = Page.objects.get(pk=page_name)
page.content = content
for tag in tag_list:
page.tags.add(tag)
except Page.DoesNotExist:
page = Page(name=page_name, content = content, pub_date = date)
for tag in tag_list:
page.tags.add(tag)
page.save()
return HttpResponseRedirect("/wikicamp/page/" + page_name +"/")
Traceback:
Environment:
Request Method: POST
Request URL: http://localhost:8080/wikicamp/page/Start/save/
Django Version: 1.3.1
Python Version: 2.7.2
Installed Applications:
['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'wikicamp.wiki',
'django.contrib.admin']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware')
Traceback:
File "C:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "D:\web dev\training\Django\secondproj\wikicamp\..\wikicamp\wiki\views.py" in save_page
80. page.tags.add(tag)
File "C:\Python27\lib\site-packages\django\db\models\fields\related.py" in add
503. self._add_items(self.source_field_name, self.target_field_name, *objs)
File "C:\Python27\lib\site-packages\django\db\models\fields\related.py" in _add_items
563. (obj, self.instance._state.db, obj._state.db))
Exception Type: ValueError at /wikicamp/page/Start/save/
Exception Value: Cannot add "<Tag: Tag object>": instance is on database "None", value is on database "default"
Are you using a router? I think your router setup is mis-configured. It looks like Django thinks the Page instance and the Tag instance you are adding to it are not associated with the same database source. This is something that would get set up in a router.
Related
How do I make sure that Many-To-Many relationships are considered in my POST-request to a Django Rest Framework API?
I have the following models:
models.py
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class Blog(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
tags = models.ManyToManyField(Tag, blank=True, related_name="blogs")
url = models.URLField(max_length=250, unique=True)
owner = models.ForeignKey(User, related_name="blogs", on_delete=models.CASCADE)
slug = models.CharField(max_length=20, default="blogs")
def __str__(self):
return self.name
And I am making the request like:
Frontend (don't mind the missing brackets)
const addContent = (content) => {
axiosInstance
.post(`/content/blogs/`, content, tokenConfig(auth.token))
.then((res) => {
dispatchMessages(
createMessage({ contentAdded: "Submitted successfully" })
);
The content object I am passing in looks like:
const content = {
name: "content title",
description: "content description",
url: "content URL",
tags: ["tag1", "tag2", "tag3"],
};
The POST request itself is going through and all the fields are posted correctly except for the tags, which appear empty.
Example Response:
{
"id": 2,
"tags": [],
"name": "Blog #1",
"description": "Its the best",
"url": "https://website.com",
},
My serializer looks like:
serializers.py
class BlogSerializer(serializers.ModelSerializer):
tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field="name")
owner = CustomOwnerField(read_only=True)
class Meta:
model = Blog
fields = "__all__"
And the viewset:
api.py
class BlogViewSet(viewsets.ModelViewSet):
permission_classes = [
permissions.IsAuthenticatedOrReadOnly
]
serializer_class = BlogSerializer
def get_queryset(self):
return Blog.objects.all()
Thank you for any tips
you have done all the tedious work. The only thing that is not allowing the tags to get saved is the read_only=True in the SlugRelatedField argument. This argument ignores the field when it is posted. So you have to remove read_only=True so that tags get parsed. I would go a little further and add queryset in the slugrelatedfield as queryset=Tags.objects.all()
This would only work if you have already created tags in your db and then you add the same names in your list. If you want to create them dynamically when you post them you have to modify the default create method in your serializer(check here)
I want to send an image array in a JSON as follows:
{"id":1,"timeIntervel":4,"images":["http://127.0.0.1:8000/images/i1.jpg","http://127.0.0.1:8000/images/i2.jpg","http://127.0.0.1:8000/images/i3.jpg","http://127.0.0.1:8000/images/i4.jpg","http://127.0.0.1:8000/images/i5.jpg"]}
I tried this with a foreignkey to my model. But failed to get a response as above.
model.py
class slidesModel(models.Model):
images = models.ImageField(upload_to='')
def __str__(self):
return str(self.images.name)
class slideImageArrayModel(models.Model):
timeIntervel=models.IntegerField()
images = models.ForeignKey(slidesModel, on_delete = models.CASCADE)
def __str__(self):
return str(self.id)
serializer.py
class slideSerializer(serializers.ModelSerializer):
class Meta:
model = slidesModel
fields='__all__'
class slideImgArraySerializer(serializers.ModelSerializer):
class Meta:
model = slideImageArrayModel
fields='__all__'
depth = 1
views.py
class slidesViewSet(viewsets.ViewSet):
def retrieve(self, request, pk=None):
queryset = slideImageArrayModel.objects.all()
user = get_object_or_404(queryset, pk=1) #it will always sends the first object
serializer = slideImgArraySerializer(user)
return Response(serializer.data)
My existing output is something which needs some modification to achieve the actual output:
{
"id": 1,
"timeIntervel": 4,
"images": {
"id": 8,
"images": "/images/b1.jpg"
}
}
I'm testing this in localhost and it's not showing complete url. I've included MEDIA_ROOT url already in settings and urls.py
Just add localhost:8000 in frontend part.
Like this code
<img src={"http://localhost:8000"+ note.img} alt="" role={'button'} onClick={(e)=> window.open(e.target.src)}/>
That will solve your issue.
Django REST Framework is reporting an error that a value is null even though I am sending the value when I POST the data.
The error Django reports is:
django.db.utils.IntegrityError: (1048, "Column 'owner_id' cannot be null")
[04/Apr/2016 18:40:58] "POST /api/items/ HTTP/1.1" 500 226814
The Angular 2 code that POSTs to Django REST Framework API is:
let body = JSON.stringify({ url: 'fred', item_type: 'P', owner_id: 2 });
let headers = new Headers();
headers.append('Content-Type', 'application/json');
this.http.post('http://127.0.0.1:8000/api/items/',
body, {
headers: headers
})
.subscribe(
data => {
alert(JSON.stringify(data));
},
err => alert('POST ERROR: '+err.json().message),
() => alert('POST Complete')
);
My Django API view looks like this:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('-date_added')
serializer_class = ItemSerializer
"""
Use the API call query params to determing what to return
API params can be:
?user=<users_id>&num=<num_of_items_to_return>&from=<user_id_of_items_to_show>
"""
def get_queryset(self):
this_user = self.request.query_params.get('user', None)
restrict_to_items_from_user_id = self.request.query_params.get('from', None)
quantity = self.request.query_params.get('num', 20)
if restrict_to_items_from_user_id is not None:
queryset = Item.objects.filter(owner=restrict_to_items_from_user_id, active=True).order_by('-date_added')[0:int(quantity)]
elif this_user is not None:
queryset = Item.objects.filter(active=True, credits_left__gt=0).exclude(pk__in=Seen.objects.filter(user_id=this_user).values_list('item_id', flat=True))[0:int(quantity)]
else:
queryset = Item.objects.filter(active=True, credits_left__gt=0)[0:int(quantity)]
print("User id param is %s and quantity is %s" % (user_id,quantity))
return queryset
The associated model is:
class Item(models.Model):
ITEM_TYPES = (
('V', 'Vine'),
('Y', 'YouTube'),
('P', 'Photo'), # Photo is stored by us on a CDN somewhere
('F', 'Flickr'),
('I', 'Instagram'),
('D', 'DeviantArt'),
('5', '500px'),
)
owner = models.ForeignKey(User, on_delete=models.CASCADE) # Id of user who owns the item
title = models.CharField(max_length=60, default='') # URL of where item resides (e.g. Vine or YouTube url)
url = models.CharField(max_length=250, default='') # URL of where item resides (e.g. Vine or YouTube url)
item_type = models.CharField(max_length=1, choices=ITEM_TYPES) # Type of item (e.g. Vine|YoutTube|Instagram|etc.)
keywords = models.ManyToManyField(Keyword, related_name='keywords')
# E.g. Art, Travel, Food, etc.
credits_applied = models.IntegerField(default=10, help_text='Total number of credits applied to this item including any given by VeeU admin')
# Records the total number of credits applied to the Item
credits_left = models.IntegerField(default=10, help_text='The number of credits still remaining to show the item')
# Number of credits left (goes down each time item is viewed
credits_gifted = models.IntegerField(default=0, help_text='The number of credits this item has been gifted by other users')
# Number of credits users have gifted to this item
date_added = models.DateTimeField(auto_now_add=True) # When item was added
liked = models.IntegerField(default=0) # Number of times this item has been liked
disliked = models.IntegerField(default=0) # Number of times this item has been disliked
active = models.BooleanField(default=True, help_text='If you mark this item inactive please say why in the comment field. E.g. "Inapproriate content"')
# True if item is available for showing
comment = models.CharField(max_length=100, blank=True) # Comment to be applied if item is inactive to say why
# Add defs here for model related functions
# This to allow url to be a clickable link
def item_url(self):
return u'%s' % (self.url, self.url)
item_url.allow_tags = True
def __str__(self):
return '%s: Title: %s, URL: %s' % (self.owner, self.title, self.url)
I can't see what is wrong with my POST call, or the Django code.
EDIT: Added serializer code
Here is the associated serializer
class ItemSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.SerializerMethodField()
def get_username(self, obj):
value = str(obj.owner)
return value
def get_keywords(self, obj):
value = str(obj.keywords)
return value
class Meta:
model = Item
fields = ('url', 'item_type', 'title', 'credits_applied', 'credits_left', 'credits_gifted', 'username', 'liked', 'disliked')
Your serializer doesn't have an owner field and your view doesn't provide one. As it's non null in your model the DB will complain about this.
You should override the view's perform_update and add the owner as extra argument to the serializer
You need to pass the Resource URI for the field which is foreign key,
here owner is the FK, so the
ownerIns = User.objects.get(id=2)
let body = JSON.stringify({ url: 'fred', item_type: 'P', owner: ownerIns, owner_id: ownerIns.id });
I ran into a similar issue using Angular and Flask. This may because your CORS headers are not set correctly for your Django app which makes your Angular app not allowed to post to the backend. In Flask, I fixed it using this code:
#app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
return response
I'm not sure how to do this in Django, but this may be a great first stop for you since this is likely your issue.
Is your ItemSerializer code correct? The rest looks fine to me.
You should be having 'owner_id' in your serializer fields i think.
Take a look at this answer, add the related fields in this manner.
https://stackoverflow.com/a/20636415/5762482
I'm using AngularJS on the front-end with Django managing the backend and API along with the Django REST framework package. I have a Project model which belongs to User and has many (optional) Intervals and Statements. I need to be able to create a 'blank' project and add any intervals/statements later, but I'm hitting a validation error when creating the project. Below are the relevant code sections.
Django model code (simplified):
class Project(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='projects', on_delete='models.CASCADE')
project_name = models.CharField(max_length=50)
class Statement(models.Model):
project = models.ForeignKey(Project, related_name='statements', on_delete='models.CASCADE', null=True, blank=True)
class Interval(models.Model):
project = models.ForeignKey(Project, related_name='intervals', on_delete='models.CASCADE', null=True, blank=True)
Django view code (simplified):
class ProjectList(APIView):
def post(self, request, format=None):
serializer = ProjectSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Angular controller code (simplified):
$scope.createProject = function(){
var projectData = {
"user": $scope.user.id,
"project_name": $scope.newProject.project_name
};
apiSrv.request('POST', 'projects', projectData,
function(data){},
function(err){}
);
};
Angular service code (simplified):
apiSrv.request = function(method, url, args, successFn, errorFn){
return $http({
method: method,
url: '/api/' + url + ".json",
data: JSON.stringify(args)
}).success(successFn);
};
Server response:
{"intervals":["This field is required."],"statements":["This field is required."]}
Am I missing something here? I should be able to create a project without a statement or interval, but I'm not able to. Thanks for any suggestions.
Edit: Added Relevant section from ProjectSerializer
class ProjectSerializer(serializers.ModelSerializer):
intervals = IntervalSerializer(many=True)
statements = StatementSerializer(many=True)
class Meta:
model = Project
fields = (
'id',
'project_name',
[removed extraneous project fields]
'user',
'intervals',
'statements'
)
You need to set the read_only attribute on the 'interval' and 'statements' fields
class ProjectSerializer(serializers.ModelSerializer):
intervals = IntervalSerializer(many=True, read_only=True)
statements = StatementSerializer(many=True, read_only=True)
class Meta:
model = Project
fields = ('id', 'project_name', 'user', 'intervals','statements')
or you can specify the read_only fields like this,
class Meta:
model = Project
fields = ('id', 'project_name', 'user', 'intervals','statements')
read_only_fields = ('intervals','statements')
Newbie question.
So I have this handler for a page that appends some CSS/JS files. The problem is that subsequent requests will result to values appended over and over again.
Example:
def action_index(self):
self.template = 'index.html'
extra_styles = ['/media/css/jquery.lightbox-0.5.css']
extra_scripts = ['/media/js/jquery.lightbox-0.5.min.js', '/media/js/project.js']
for style in extra_styles:
self.styles.append(style)
for script in extra_scripts:
self.scripts.append(script)
How do you usually handle this in platform like Google AppEngine since I'm coming from PHP background where objects only lives within the current request.
Thanks
As requested, here is the base class:
Base controller
from google.appengine.ext.webapp import template
import os
import config
import datetime
class BaseController(object):
request = None
response = None
action = 'index'
method = 'get'
params = []
template_values = {}
template_dir = None
template = None
default_styles = ['/media/bootstrap/css/bootstrap.min.css', '/media/css/style.css']
default_scripts = ['/media/js/jquery-1.6.4.min.js']
styles = []
scripts = []
def __init__(self, request, response, *args, **kwargs):
self.request = request
self.response = response
self.action = 'index'
if 'action' in kwargs and kwargs['action']:
self.action = kwargs['action']
self.method = 'get'
if 'method' in kwargs and kwargs['method']:
self.method = kwargs['method']
self.params = []
if 'params' in kwargs and kwargs['params']:
if isinstance(kwargs['params'], list):
self.params = kwargs['params']
# Initialize template related variables
self.template_values = {}
self.styles = list(self.default_styles)
self.scripts = list(self.default_scripts)
def pre_dispatch(self):
pass
def post_dispatch(self):
if self.template is not None and self.template_dir is not None:
# Populate current year
dt = datetime.date.today()
self.template_values['current_year'] = dt.year
# Populate styles and scripts
self.template_values['styles'] = self.styles
self.template_values['scripts'] = self.scripts
path = os.path.join(config.template_dir, self.template_dir, self.template)
self.response.out.write(template.render(path, self.template_values))
def run_action(self):
action_name = 'action_' + self.action
if hasattr(self, action_name):
action = getattr(self, action_name)
action()
else:
raise Http404Exception('Controller action not found')
def dispatch(self):
self.pre_dispatch()
self.run_action()
self.post_dispatch()
class HttpException(Exception):
"""Http Exception"""
pass
class Http404Exception(HttpException):
"""Http 404 exception"""
pass
class Http500Exception(HttpException):
"""Http 500 exception"""
pass
And the child class
import datetime
from dclab.lysender.controller import BaseController
class ProjectsController(BaseController):
template_dir = 'projects'
def action_index(self):
self.template = 'index.html'
self.styles.extend(['/media/css/jquery.lightbox-0.5.css'])
self.scripts.extend([
'/media/js/jquery.lightbox-0.5.min.js',
'/media/js/project.js'
])
My fault was that I'm assigning a list to another list via reference, not cloning the list. I'm not so aware of this behavior that's why it made me scratching my head the whole night.
You're declaring a whole lot of variables directly under the class definition. Doing this does not define instance members, as you seem to be expecting, but rather defines class variables - the equivalent of 'static' in a language like Java.
Instead of declaring things as class variables, initialize your values inside the constructor - the __init__ method of your class.
I would also strongly recommend using an existing web framework like webapp2 rather than inventing your own.