Save in Django Admin sometimes fails - django

I have a portfolio page with django, and i'm noticing a strange behaviour when a try to save an edited entry. It saves, and then it seems that the redirect fails to catch the ID and then goes to a 404 page that says domain.com/admi/internal_error.html/ in the URL (note the admi, sometimes is ad or adm). It happens with both 'Save' and 'Save and continue editing'.
This is my model
from django.db import models
from ramonlapenta.portfolio.managers import EntryManager
from ramonlapenta.slughifi import slughifi
class Type(models.Model):
type = models.CharField(max_length=100)
def __unicode__(self):
return self.type
class Entry(models.Model):
type = models.ForeignKey(Type)
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100)
slug = models.SlugField()
description = models.TextField()
requirement = models.TextField()
solution = models.TextField()
url = models.CharField(blank=True, max_length=64)
published = models.BooleanField(db_index=True, default=True)
logo = models.ImageField(upload_to="logos")
image = models.ImageField(upload_to="portfolio", height_field='height', width_field='width')
height = models.CharField(max_length=3)
width = models.CharField(max_length=3)
objects = EntryManager()
def __unicode__(self):
return u"%s - %s" % (self.title, self.created)
class Meta:
verbose_name_plural = "entries"
def save(self, *args, **kwargs):
self.slug = slughifi(self.title)
super(Entry, self).save(*args, **kwargs)
And this is the admin.py
from django.contrib import admin
from ramonlapenta.portfolio.models import Type
from ramonlapenta.portfolio.models import Entry
from django import forms
class EntryAdmin(admin.ModelAdmin):
fieldsets = [
('Title', {'fields' : ['title']}),
('Address', {'fields' : ['url']}),
('Description', {'fields' : ['description','requirement','solution']}),
('Type', {'fields' : ['type']}),
('Image', {'fields' : ['logo', 'image']}),
]
list_display = ('title', 'created')
list_filter = ['created', 'type']
admin.site.register(Type)
admin.site.register(Entry, EntryAdmin)
This is my entry manager, where i only filter the results.
from django.db import models
class EntryManager(models.Manager):
def published(self):
return self.model.objects.filter(published=True).order_by('-id')
This is the general urls.py
from django.conf.urls.defaults import patterns, include, url
from django.conf.urls.defaults import *
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
(r'^', include('site.pages.urls')),
(r'^', include('site.blog.urls')),
(r'^', include('site.portfolio.urls')),
(r'^admin/', include(admin.site.urls)),
)
And the portfolio urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('ramonlapenta.portfolio.views',
url(r'^portfolio/$', 'index', name="port-main"),
url(r'^portfolio/(?P<item_slug>[a-zA-Z0-9_.-]+)/$', 'detail', name="port-slug"),
url(r'^portfolio/type/(?P<type>[a-zA-Z0-9_.-]+)/$', 'type', name="port-type")
)
Thos is what i see with HttpFox:
Note: everything else works fine, even there it sometimes works fine, and the public site works fine too. The problem is just editing a portfolio entry.
Does anybody knows why is this happening and how can i fix it?

My guess is that the main url.py is catching some of the admin data (being that any admin action will be considered by all the other urlconfs.
Try to put the admin urls at the top:
urlpatterns = patterns('',
(r'^admin/', include(admin.site.urls)),
(r'^', include('site.pages.urls')),
(r'^', include('site.blog.urls')),
(r'^', include('site.portfolio.urls')),
)
Edit: second guess
The key information is that second 302 in the request list. Someone (probably dreamost's apache) is eating the response and returning a redirect to a custom page.
In this case setting DEBUG = True is of no help, because the response would have anyway the same error.
This means two things:
You have an error somewhere in your Model's code, probably some attribute/field/property that's used to display the object.
To isolate the error, try to find a pattern on which specific values trigger the error and which values allows to load the admin form.
You can't see the django error page unless you either work locally in the development server (manage.py runserver) or disable custom error pages in dreamhost as other suggest.

Knowing your setup and what appears in server logs at the time you're saving an entry might help us locate a problem. It's likely that the cause is outside Django (since the bug is not reproducible under Django's development server).
Anyway, you might find some of these solutions relevant:
Django on dreamhost problem (old post, but symptoms described there are similar, might be relevant if you're running Django on FastCGI).
getting 500 internal server error with django on dreamhost (in short: just try re-create the whole setup and see if that helps).
Or you may want simply to google dreamhost django 500 in case you haven't already, seems like people frequently encounter similar problem while setting up Django on Dreamhost.

I'll be curious to see your urls.py . My guess is that the regex for the admin site is bogus.

For sure an error is happening when you save those entries and you should check your logs. Or try reproduce it with DEBUG=True
My guess is that you are using dreamhost and Dreamhost allows you to define custom error pages and "internal_error.html" is for the status 500 error page (see : http://wiki.dreamhost.com/Creating_custom_error_pages).

Try to have a look at your dreamhost error.log file.
The sure thing is that the problem comes from your dreamhost configuration and not your django code (since it is working properly in development with Django 1.2)

Related

How to solve the Identical URL Pattern Conflict in Django?

while practicing on Django I have faced the following problem of Identical URL Pattern. I have tried but fail to solve.
My project has one apps and the models are
class Category(models.Model):
title = models.CharField(max_length=150)
slug = models.SlugField(unique=True,blank=True, max_length=255)
class Post(models.Model):
title = models.CharField(max_length=200)
cats = models.ForeignKey(Category, on_delete=models.CASCADE)
slug = models.SlugField(unique=True,blank=True, max_length=255)
My urls.py
from django.contrib import admin
from django.urls import path
from post import views as apps
urlpatterns = [
path('admin/', admin.site.urls),
path('<slug:slug>/', apps.categoryView, name='category'),
path('<slug:slug>/',apps.PostView, name='calc_detail'),
]
Problem:
When I Put '/' in the second line of the urlpattern for Category View, category View works but post View doesn't work (404).
If remove '/' urlpattern for CategoryView , then post view Works but Category Views shows 404.
How should I configure these urls. I am using function based views
There's really no way to handle these except using different patterns or handling both in the same view, I would suggest different patterns:
from django.contrib import admin
from django.urls import path
from post import views as apps
urlpatterns = [
path('admin/', admin.site.urls),
path('pattern1/<slug:slug>/', apps.categoryView, name='category'),
path('pattern2/<slug:slug>/',apps.PostView, name='calc_detail'),
]
Also have you thought what would happen if a post and a category ended up having the same slug? Best to have different patterns.
About putting '/' making some url work and not it is because django appends slashes by default to each url, so if you really want to do this (NOT RECOMMENDED) you may set APPEND_SLASH = False in your setting and have one url with a slash and other without.

how to redirect from HomePage(Page) to your custom app model

I've created an app "Blog". In my app I've got several models including "BlogIndex(Page)". When I run local server I find myself at "home_page.html". What I want is to start my local server at "blog_index.html". I know that I can set a root page at settings>site>localhost to make my "blog_index.html" a root page, but I can't do this because in my app I've got some other models that live at the same level as "BlogIndex(Page)" and they are children of the root which is "HomePage" so it would brake my code. So my question is: can I make a redirect from "HomePage(Page)" to my "BlogIndex" so that when i start my server I would be automatically redirected from "HomePage" to "BlogIndex"? How can I do it? How much it will affect the performance of the site and it's optimization?
I know that there is settings>redirect but it works only for inactive pages, but i need "HomePage" to be active.
Thank you.
Perhaps a better approach would be to display your blog posts (and any other models you want) on your homepage. Just override get_context(). See here: Wagtail Views: extra context
Update:
You can redirect by overriding the serve() method. For example, in your model, you would do something like:
# home/models.py
...
from django.http import HttpResponseRedirect
from django.urls import reverse
class HomePage(Page):
body = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('body', classname="full"),
]
def serve(self, request):
# Redirect to blog index page
return HttpResponseRedirect('/blog/')
# only do this if you're using urls.py and namespaces
# return HttpResponseRedirect(reverse('blog:index'))
More info: http://docs.wagtail.io/en/latest/reference/pages/model_recipes.html?highlight=serve()#overriding-the-serve-method

Multiple Django admin sites redirects to each other

firstly, sorry if this problem was already solved, but I couldn't find solution anywhere.
I created two admin instances in Django, here's my admin.py sample code:
class ConferenceRoomAdmin(admin.ModelAdmin):
readonly_fields = ('token', 'image_tag')
class ConferenceRoomSuperAdmin(admin.ModelAdmin):
readonly_fields = ('token', 'image_tag')
class ConferenceAdmin(admin.AdminSite):
def get_urls(self):
urls = super(ConferenceAdmin, self).get_urls()
return urls
admin_site = ConferenceAdmin()
admin_site.register(ConferenceContext)
admin_site.register(ConferenceRoom, ConferenceRoomAdmin)
class ConferenceSuperAdmin(admin.AdminSite):
pass
super_admin_site = ConferenceSuperAdmin()
super_admin_site.register(ConferenceRoom, ConferenceRoomSuperAdmin)
and urls.py file:
url(r'^admin/', include(admin.admin_site.urls)),
url(r'^myadmin/', include(admin.super_admin_site.urls)),
url(r'^login/', views.login_view),
url(r'^test/', views.test_view),
I'm able to login to both admin instances, however, if I log into 'myadmin' instance and click on any link (i.e. change password or edit users, etc. etc) I'm redirected to 'admin' site. What may be wrong here?
Thanks in advance!
Try adding a name attribute to the second admin site, like so:
super_admin_site = ConferenceSuperAdmin(name = 'super_admin')
That is the only difference between your code and mine, as far as I can tell.

Best practice for Django sites to set up site configuration variables?

all! I am writing a Django blog site.
I am new to Django. Since Django has the philosophy of loose coupling, I believe it's best to follow their beliefs when using their framework.
So I am encountering a dilemma here:
I want to set up some variables for my blog, say, the blog title, slogan, and the maximum length of digest on the homepage of each blog I wrote, and how many blog digest should I display on the homepage per page.
I can do this by creating a new app in my Django project, and create models for my site's config variables, then read those variables from other app, but this practice obviously breaks the philosophy of loose coupling.
The only work around I can think of is setting up environment variables in my sites .wsgi file (I use Apache and mod_wsgi to serve Python scripts) But I don't think messing up with environment variable is 'clean' enough.
Can anyone suggest me a better solution?
First thing you can do is set up those variables in your project's settings module, many apps do that:
# settings
BLOG_TITLE = 'My title'
Then, a good practice is to provide settings defaults, so your app should have a settings file
# blog/settings.py
from django.conf import settings
BLOG_TITLE = getattr(settings, 'BLOG_TITLE', 'MY default title')
# Then wherever, views or context processor
from blog import settings # See how we're not importing project's settings
title = settings.BLOG_TITLE
Another alternative is to create a "Blog" model that contains all those variables, this may invite you to make your app to have a blog tied per Django sites
from django.contrib.sites.models import Site
class Blog(models.Model):
site = models.OneToOneField(Site) # Tied up to a Django site
title = models.CharField(max_length=256)
Now you can change those values from your Admin interface and use them in your views or context processor
site = Site.objects.get_current() # Installed Django Sites app and middleware
blog = site.blog
print blog.title
You can create settings or constants file in your app folder and use it. This way all your constants will be bound to your application:
apps/
blog/
__init__.py
models.py
urls.py
constants.py # <-- here it is!
File can look like this:
BLOG_TITLE = 'My super blog'
# ...
And you can use tour constants like this:
import blog.constants
# ...
return render_to_response('index.html', {title: blog.constants.BLOG_TITLE})
And in template:
<title>{{ title }}</title>
In my opinion, the best way to do this is by adding a model related to Site model using inheritance
First add site id to your Django settings file
SITE_ID = 1
now create a model in an app
from django.db import models
from django.contrib.sites.models import Site
class Settings(Site):
field_a = models.CharField(max_length=150, null=True)
field_b = models.CharField(max_length=150, null=True)
class Meta:
verbose_name_plural = 'settings'
db_table = 'core_settings' # core is name of my app
def __str__(self) -> str:
return 'Settings'
then edit apps.py file of that app
from django.apps import AppConfig
from django.db.models.signals import post_migrate
def build_settings(sender, **kwargs):
from django.contrib.sites.models import Site
from .models import Settings
if Settings.objects.count() < 1:
Settings.objects.create(site_ptr=Site.objects.first())
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'project.apps.core'
def ready(self) -> None:
post_migrate.connect(build_settings, sender=self)
now every time you run migrations a row will auto-generated in core_settings that have a one to one relationship with your Site model
and now you can access to your settings like this
Site.objects.get_current().settings.access_id
optional: if have only a single site
unregister site model from admin site and disable creating and deleting settings model in admin panel
from django.contrib import admin
from . import models
from django.contrib.sites.models import Site
admin.site.unregister(Site)
#admin.register(models.Settings)
class SettingAdminModel(admin.ModelAdmin):
def has_delete_permission(self, request,obj=None) -> bool:
return False
def has_add_permission(self, request) -> bool:
return False

Call an API on my server from another view

So, kind of a in a weird situation here. I've got a Django project using TastyPie to power its API and some views/templates that will be used to power plugins for various sites. Rather than building this plugins as standard Django templates, I've been asked to use our API to process requests to the plugins, which means I'm calling a view on my server from another view, and for some reason that isn't working with any of my views. For reference:
#views.py
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template.context import Context, RequestContext
import json, urllib, urllib2
SITE_NAME = "http://localhost:8000/"
API_PATH = "api/v1/"
def aggregate(request):
template = 'lemonwise/plugins/aggregate.html'
sku = request.GET.get('sku')
url = ''.join([SITE_NAME, API_PATH, 'product/?sku=', sku])
product = json.loads(urllib.urlopen(url).read())['objects'][0]
return render_to_response(template, product)
def reviews(request):
template = 'lemonwise/plugins/reviews.html'
sku = request.GET.get('sku')
url = ''.join([SITE_NAME, API_PATH, 'product/?format=json&sku=', sku])
#Comment the next line out and the url is passed correctly
response = urllib2.build_opener().open(url).read()
return HttpResponse(url)
#page = opener.open(url).read()
#return HttpResponse(url)
#product = json.loads(urllib2.build_opener().open(url).read())['objects'][0]
#return HttpResponse(url)
#reviews = [json.loads(urllib.urlopen(SITE_NAME + uri)) for uri in product['reviews']]
#return render_to_response(template, {'reviews': reviews})
def survey(request):
template = 'lemonwise/plugins/survey.html'
sku = request.GET.get('sku')
url = ''.join([SITE_NAME, API_PATH, 'product/?sku=', sku])
product = json.loads(urllib2.build_opener().open(url).read())['objects'][0]
return render_to_response(template, product)
def mosthelpfulpositive(request):
template = 'lemonwise/plugins/mosthelpfulpositive.html'
sku = request.GET.get('sku')
url = ''.join([SITE_NAME, API_PATH, 'product/?sku=', sku])
product = json.loads(urllib2.build_opener().open(url).read())['objects'][0]
uri = product['most_helpful_positive']
most_helpful_positive = json.loads(urllib.urlopen(SITE_NAME + uri))
return render_to_response(template, most_helpful_positive)
def mosthelpfulnegative(request):
template = 'lemonwise/plugins/mosthelpfulnegative.html'
sku = request.GET.get('sku')
url = ''.join([SITE_NAME, API_PATH, 'product/?sku=', sku])
product = json.loads(urllib2.build_opener().open(url).read())['objects'][0]
uri = product['most_helpful_negative']
most_helpful_negative = json.loads(urllib.urlopen(SITE_NAME + uri))
return render_to_response(template, most_helpful_negative)
And the corresponding urls.py (in a different app):
#urls.py
from django.conf import settings
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from tastypie.api import Api
from lemonwise.reviews.api import *
admin.autodiscover()
v1_api = Api(api_name='v1')
v1_api.register(UserResource())
v1_api.register(MerchantResource())
v1_api.register(ProductFamilyResource())
v1_api.register(ProductResource())
v1_api.register(BooleanAttributeResource())
v1_api.register(SlideAttributeResource())
v1_api.register(ProductAttributeResource())
v1_api.register(SubmissionResource())
v1_api.register(ReviewResource())
v1_api.register(ReviewProductAttributeResource())
v1_api.register(CommentResource())
v1_api.register(BestUseResource())
v1_api.register(HelpfulVoteResource())
#Acess the api via http://127.0.0.1:8000/api/v1/user/?format=json
urlpatterns = patterns('',
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
# Lemonwise apps
url(r'^reviews/', include('lemonwise.reviews.urls')),
#API
url(r'^api/', include(v1_api.urls)),
)
if settings.DEBUG:
urlpatterns += staticfiles_urlpatterns()
Any idea on how to fix this? I can't find any writings on the subject.
EDIT: More specifically, what's happening is that when I load any of these views they hang trying to read the api page. ALso, my server shows no indications of any requests being processed. (Though I can load the api pages directly.)
Now your comment has clarified the situation, I can take a guess at what the problem is. It is that you are using the built-in development server, which is single-threaded. So while it's processing the original request, it's not able to process the internal request for another URL - so it hangs indefinitely.
The solution, as Mao points out, is to think of a better architecture. If you can't do that, you may have some luck with using something like gunicorn instead of the built-in server.
Particular to Tastypie you can use your Resources in views, if you need to, accessing the objects directly and calling the get methods and building the bundle.
You can find out how: http://django-tastypie.readthedocs.org/en/latest/cookbook.html
Go to the section titled - Using Your Resource In Regular Views
Agreed, your development server likely only has one instance. Launch another one on a different port. ie/
SITE_NAME = "http://localhost:8001/"
Okay, so I already said that having views making HTTPRequests against other views is kind of silly, but I do see what your problem currently is. You are calling json.loads(urllib.urlopen(SITE_NAME + uri)), but urllib.urlopen returns a file-like object, not a string, so it should actually be json.load(urllib.urlopen(SITE_NAME + uri)).
I just encountered the same problem i.e. Calling an API from the same server it is hosted. As Daniel suggested, I was able to solve this by using multi threads in gunicorn:
gunicorn --certfile=crt --keyfile=key --bind 0.0.0.0:8000 app.wsgi --threads 5