I want to get a list of all pages I have published in my Django-CMS application but I can't seem to get it to render the language-specific urls.
in my settings I have specified:
LANGUAGES = [
('sv', 'Svenska'),
('en', 'English'),
]
I go in to the admin site, I create a contact-page (That is not site-home) and set different slugs on different languages, 'contact-sv' and 'contact-en' for instance.
Then I have a view that gets a page
from cms.models import Page
page = Page.objects.published()[1]
print 'swe:', page.get_absolute_url(language='sv')
print 'eng:', page.get_absolute_url(language='en')
I then get This output:
swe: /contact-en/
eng: /contact-en/
When I expected the swe-url to be /sv/contact-sv/
I don't need the langage prefix to the path, I can prepend that to the path but I need the language specific path.
I can offer you no better solution than to wait for the 2.4 release which will use Django's built-in i18n_urlpatterns to handle this, which should fix the issue you have, for now, your answer is the only way to go.
I bet there is a better way to do it but now I loop through titles on the page object.
for title in page.title_set.all():
print title.language, title.slug
It works, but I do not like it...
Maybe you could use the sitemap strategy. django cms has it's own sitemap class that runs this code:
class CMSSitemap(Sitemap):
changefreq = "monthly"
priority = 0.5
def items(self):
all_titles = Title.objects.public().filter(
Q(redirect='') | Q(redirect__isnull=True),
page__login_required=False,
page__site=Site.objects.get_current(),
).order_by('page__path')
return all_titles
Related
I am trying to redirect the different pages for the Facebook and Twitter in Django 1.11.5 using the Django-Allauth.
My settings.py
I have tried something like this but it didn't work in my case:
import allauth.socialaccount.providers as ASP
if ASP == 'facebook':
LOGIN_REDIRECT_URL = '/test/'
else:
LOGIN_REDIRECT_URL = '/'
I want to know how I can redirect the url for different social media on different links?
Kindly suggested me the improvements in the code.
You could use the method described in the official documentation:
https://django-allauth.readthedocs.io/en/latest/advanced.html?highlight=LOGIN_REDIRECT_URL#custom-redirects
That does exactly what you want.
Or use a simple workaround that would be to define a route in LOGIN_REDIRECT_URL that would then forward/redirect the user to the correct page according to sign in method or social account type. Like this:
LOGIN_REDIRECT_URL = '/after-login-example/'
then:
# view for that url
def example_view(request):
account = SocialAccount.objects.get(user=request.user)
if account.provider == "Twitter":
return HttpResponseRedirect(<your twitter page>)
elif:
...
Note1: I didn't test the above example and it assumes that the user has one or another method (not both), but even if the condition is different it exemplifies the overall approach.
Note2: The first (and official) one is obviously a better solution.
I'm making an admin panel for a Django-Mptt tree structure using the FeinCMS TreeEditor interface. This interface provides an 'actions column' per-node for things like adding or moving nodes quickly without using the typical Django admin action select box.
What I am trying to do is add a custom admin action to this collection which passes the pk of the node to a celery task which will then add a collection of nodes as children. Existing functions are simply href links to the URL for that task(add/delete/move), so thus far I have simply mimicked this.
My solution currently involves:
Define the action as a function on the model
Create a view which uses this function and redirects back to the changelist
Add this view to the admin URLs
Super the TreeEditor actions column into the ModelAdmin class
Add an action to the collection which calls this URL
Surely there must be a better method than this? It works, but it feels massively convoluted and un-DRY, and I'm sure it'll break in odd ways.
Unfortunately I'm only a month or two into working with Django so there's probably some obvious functions I could be using. I suspect that I might be able to do something with get_urls() and defining the function directly in the ModelAdmin, or use a codeblock within the injected HTML to call the function directly, though I'm not sure how and whether it's considered a better option.
Code:
I've renamed everything to a simpler library <> books example to remove the unrelated functionality from the above example image.
models.py
class Library(models.Model):
def get_books(self):
# Celery task; file omitted for brevity
get_books_in_library.delay(self.pk)
views.py
def get_books_in_library(request, library_id):
this_library = Library.objects.get(pk=library_id)
this_library.get_books_in_library()
messages.add_message(request, messages.SUCCESS, 'Library "{0}" books requested.'.format(this_library.name))
redirect_url = urlresolvers.reverse('admin:myapp_library_changelist')
return HttpResponseRedirect(redirect_url)
urls.py
urlpatterns = [
url(r'^admin/myapp/library/(?P<library_id>[0-9]+)/get_books/$', get_books_in_library, name='get books in library'),
url(r'^admin/', include(admin.site.urls)),
]
admin.py
class LibraryAdmin(TreeEditor):
model = Library
def _actions_column(self, obj):
actions = super(LibraryAdmin, self)._actions_column(obj)
actions.insert(
0, u'<a title="{0}" href="{1}/get_books"><img src="{2}admin/img/icon_addlink.gif" alt="{0}" /></a>'.format(
_('Get Books'),
obj.pk,
settings.STATIC_URL
)
)
return actions
Note that I may have broken something in renaming things and removing the extraneous cruft if you try to execute this code, I think it should adequately illustrate what I'm trying to do here however.
After digging around today and simply trying various other solutions, I've put together one that uses get_urls and a view defined directly into the admin interface which feels tidier though it's effectively just moving the code from multiple django files into the admin interface - though it does make use of the admin wrapper to stop unauthenticated users, which is an improvement.
I'll leave a copy of the working code here for anyone who finds this in future, as I've seen very few examples of TreeEditor et al. being used in newer versions of Django.
class NodeAdmin(TreeEditor):
model = Node
# < ... > Other details removed for brevity
def get_urls(self):
urls = super(NodeAdmin, self).get_urls()
my_urls = [
url(r'^(?P<node_id>[0-9]+)/get_suggestions/$', self.admin_site.admin_view(self.get_suggestions)),
]
return my_urls + urls
def get_suggestions(self, request, node_id):
this_node = Node.objects.get(pk=node_id)
get_suggestions(this_node.pk)
messages.add_message(request, messages.SUCCESS, 'Requested suggestions for {0}'.format(this_node.term))
redirect_url = urlresolvers.reverse('admin:trinket_node_changelist')
return HttpResponseRedirect(redirect_url)
def _actions_column(self, obj):
actions = super(NodeAdmin, self)._actions_column(obj)
# Adds an 'get suggestions' action to the Node editor using a search icon
actions.insert(
0, u'<a title="{0}" href="{1}/get_suggestions"><img src="{2}admin/img/selector-search.gif" alt="{0}" /></a>'.format(
_('Get Suggestions'),
obj.pk,
settings.STATIC_URL,
)
)
# Adds an 'add child' action to the Node editor using a plus icon
actions.insert(
0, u'<a title="{0}" href="add/?{1}={2}"><img src="{3}admin/img/icon_addlink.gif" alt="{0}" /></a>'.format(
_('Add child'),
getattr(self.model._meta,'parent_attr', 'parent'),
obj.pk,
settings.STATIC_URL
)
)
return actions
I'm in the process of redesigning a site which is being converted from ASPX to django. We'd like to redirect old urls like this:
/detail.aspx?ID=123
...to...
/articles/123
The problem is in the url pattern matching. I'd like to match the pattern of the old url like so:
(r'^detail.aspx?ID=(?P\d+)$', 'mygreatview'),
...and from there do a 301 redirect in the view:
def mygreatview(request, url_id):
article = get_object_or_404(Article, url_id=url_id)
url = '/articles/' + article.url_id
return HttpResponsePermanentRedirect(url)
The problem is that Django is appending a slash, so the old url gets converted to:
/detail.aspx/?ID=123
...before it gets interpreted by the urls.py.
I tried setting APPEND_SLASH = False, but that seems to cause other problems (squeezing the baloon here) with the other urls.
What am I missing? Should I be using Apache and mod_rewrite to handle this scenario?
Thanks in advance.
That is not your problem. Your problem is that query parameters (?ID=123) are not part of the URL. You should just match against /detail.aspx/ and get the parameters in the view with request.GET['ID'].
Actually, you shouldn't do that at all. This level of redirection is much better handled by your web server configuration, eg with mod_rewrite in Apache. There's no need to invoke the overhead of a Django view to do this sort of thing.
I got this to work, but I'm still not completely happy with it b/c it's a "2-step hop" for the SEO bots. Here's what I've got:
In Apache conf:
RedirectMatch 301 ^/detail\.aspx(.*) /article$1
This sends the querystring as a parameter to a view:
def mygreatview(request):
ID = request.GET['ID']
article = get_object_or_404(Article, url_id=ID)
url = '/articles/' + article.URL
return HttpResponsePermanentRedirect(url)
Wish I could do it in 1 hop, but I've got to use the ID to lookup the slug. Don't see how I can get around that one, but should be fine.
Why not the following? (haven't tested)
RewriteCond %{QUERY_STRING} ^ID=(\w+)$
RewriteRule ^/detail.aspx /articles/%1?
see http://wiki.apache.org/httpd/RewriteQueryString for more examples.
This will take your old URLs and redirect them to your django view like it expects without having to put any hacky code in your django view.
Is there a way to get the complete django url configuration?
For example Django's debugging 404 page does not show included url configs, so this is not the complete configuration.
Django extensions provides a utility to do this as a manage.py command.
pip install django-extensions
Then add django_extensions to your INSTALLED_APPS in settings.py. then from the console just type the following
python manage.py show_urls
Django is Python, so introspection is your friend.
In the shell, import urls. By looping through urls.urlpatterns, and drilling down through as many layers of included url configurations as possible, you can build the complete url configuration.
import urls
urls.urlpatterns
The list urls.urlpatterns contains RegexURLPattern and RegexURLResolver objects.
For a RegexURLPattern object p you can display the regular expression with
p.regex.pattern
For a RegexURLResolver object q, which represents an included url configuration, you can display the first part of the regular expression with
q.regex.pattern
Then use
q.url_patterns
which will return a further list of RegexURLResolver and RegexURLPattern objects.
At the risk of adding a "me too" answer, I am posting a modified version of the above submitted script that gives you a view listing all the URLs in the project, somewhat prettified and sorted alphabetically, and the views that they call. More of a developer tool than a production page.
def all_urls_view(request):
from your_site.urls import urlpatterns #this import should be inside the function to avoid an import loop
nice_urls = get_urls(urlpatterns) #build the list of urls recursively and then sort it alphabetically
return render(request, "yourapp/links.html", {"links":nice_urls})
def get_urls(raw_urls, nice_urls=[], urlbase=''):
'''Recursively builds a list of all the urls in the current project and the name of their associated view'''
from operator import itemgetter
for entry in raw_urls:
fullurl = (urlbase + entry.regex.pattern).replace('^','')
if entry.callback: #if it points to a view
viewname = entry.callback.func_name
nice_urls.append({"pattern": fullurl,
"location": viewname})
else: #if it points to another urlconf, recur!
get_urls(entry.url_patterns, nice_urls, fullurl)
nice_urls = sorted(nice_urls, key=itemgetter('pattern')) #sort alphabetically
return nice_urls
and the template:
<ul>
{% for link in links %}
<li>
{{link.pattern}} ----- {{link.location}}
</li>
{% endfor%}
</ul>
If you wanted to get real fancy you could render the list with input boxes for any of the regexes that take variables to pass to the view (again as a developer tool rather than production page).
This question is a bit old, but I ran into the same problem and I thought I would discuss my solution. A given Django project obviously needs a means of knowing about all its URLs and needs to be able to do a couple things:
map from a url -> view
map from a named url -> url (then 1 is used to get the view)
map from a view name -> url (then 1 is used to get the view)
Django accomplishes this mostly through an object called a RegexURLResolver.
RegexURLResolver.resolve (map from a url -> view)
RegexURLResolver.reverse
You can get your hands on one of these objects the following way:
from my_proj import urls
from django.core.urlresolvers import get_resolver
resolver = get_resolver(urls)
Then, you can simply print out your urls the following way:
for view, regexes in resolver.reverse_dict.iteritems():
print "%s: %s" % (view, regexes)
That said, Alasdair's solution is perfectly fine and has some advantages, as it prints out some what more nicely than this method. But knowing about and getting your hands on a RegexURLResolver object is something nice to know about, especially if you are interested in Django internals.
The easiest way to get a complete list of registered URLs is to install contrib.admindocs then check the "Views" section. Very easy to set up, and also gives you fully browsable docs on all of your template tags, models, etc.
I have submitted a package (django-showurls) that adds this functionality to any Django project, it's a simple new management command that integrates well with manage.py:
$ python manage.py showurls
^admin/
^$
^login/$
^logout/$
.. etc ..
You can install it through pip:
pip install django-showurls
And then add it to your installed apps in your Django project settings.py file:
INSTALLED_APPS = [
..
'django_showurls',
..
]
And you're ready to go.
More info here -
https://github.com/Niklas9/django-showurls
If you want a list of all the urls in your project, first you need to install django-extensions
You can simply install using command.
pip install django-extensions
For more information related to package goto django-extensions
After that, add django_extensions in INSTALLED_APPS in your settings.py file like this:
INSTALLED_APPS = (
...
'django_extensions',
...
)
urls.py example:
from django.urls import path, include
from . import views
from . import health_views
urlpatterns = [
path('get_url_info', views.get_url_func),
path('health', health_views.service_health_check),
path('service-session/status', views.service_session_status)
]
And then, run any of the command in your terminal
python manage.py show_urls
or
./manage.py show_urls
Sample output example based on config urls.py:
/get_url_info django_app.views.get_url_func
/health django_app.health_views.service_health_check
/service-session/status django_app.views.service_session_status
For more information you can check the documentation.
Are you looking for the urls evaluated or not evaluated as shown in the DEBUG mode? For evaluated, django.contrib.sitemaps can help you there, otherwise it might involve some reverse engineering with Django's code.
When I tried the other answers here, I got this error:
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
It looks like the problem comes from using django.contrib.admin.autodiscover() in my urls.py, so I can either comment that out, or load Django properly before dumping the URL's. Of course if I want to see the admin URL's in the mapping, I can't comment them out.
The way I found was to create a custom management command that dumps the urls.
# install this file in mysite/myapp/management/commands/urldump.py
from django.core.management.base import BaseCommand
from kive import urls
class Command(BaseCommand):
help = "Dumps all URL's."
def handle(self, *args, **options):
self.show_urls(urls.urlpatterns)
def show_urls(self, urllist, depth=0):
for entry in urllist:
print ' '.join((" " * depth, entry.regex.pattern,
entry.callback and entry.callback.__module__ or '',
entry.callback and entry.callback.func_name or ''))
if hasattr(entry, 'url_patterns'):
self.show_urls(entry.url_patterns, depth + 1)
If you are running Django in debug mode (have DEBUG = True in your settings) and then type a non-existent URL you will get an error page listing the complete URL configuration.
Consider that I include namespaced reusable application:
urlpatterns = patterns('',
# ella urls
url('^ella/', include('ella.core.urls', namespace="ella")),
)
Now, the Ella applications has urls like that:
urlpatterns = patterns( '',
url( r'^(?P<category>[a-z0-9-/]+)/$', category_detail, name="category_detail" ),
# object detail
url( r'^(?P<category>[a-z0-9-/]+)/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<content_type>[a-z0-9-]+)/(?P<slug>[a-z0-9-]+)/$',
object_detail, name="object_detail" )
)
Now, calling {% url ella:category_detail category="cat" %} works fine. However, when object tries to generate link to it's details, it calls
from django.core.urlresolvers import reverse
url = reverse('object_detail', kwargs={'required' : 'params'})
This is not working, unless rewritten as
from django.core.urlresolvers import reverse
url = reverse('ella:object_detail', kwargs={'required' : 'params'})
So, if I understand it correctly, including reusable application into namespace breaks all inner reverse()s inside given application.
Is it true? What have I missed? Is there any way around?
Since you have name-spaced url configuration, you need to mention namespace:view-name pattern in order to reverse it properly (especially from view).
But, if you want to avoid this, you may also pass namespace/appname as current_app parameter.
There are multiple ways to specify current_app when you are in template. But if you are in view, you need to hard-code as you did, or pass to current_app parameter
url = reverse('object_detail',
kwargs={'foo':'bar'},
current_app=app_name_or_name_space)
refer: http://docs.djangoproject.com/en/dev/topics/http/urls/#reverse
URL Namespaces can be specified in two ways.
Firstly, you can provide the application and instance namespace as arguments to include() when you construct your URL patterns. For example,:
(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
This is right from http://docs.djangoproject.com/en/dev/topics/http/urls/#defining-url-namespaces.
Try changing include('ella.core.urls', namespace="ella") to include('ella.core.urls', namespace="ella", app_name="ella"). I'm not 100% this will work, but its worth a shot.
At least in Django 1.8 you can write something like this:
url = reverse('%s:object_detail' % request.resolver_match.namespace, kwargs={'required' : 'params'})
request.resolver_match.namespace is a string containing the namespace of the currently running view.