I'm new to wagtail and pretty new to django. I'm wondering how to implement the blog that's documented here:
https://docs.wagtail.io/en/stable/getting_started/tutorial.html
but directly within the home page. Meaning, I'd like the blog index to be the root of the site (like most blogging sites have).
Thanks in advance!
You could just add your blog 'posts' (e.g. BlogPage) as direct children under the home page.
This will mean your blog page URLs will be directly under the root URL.
e.g. mydomain.com/my-cool-post/.
Note: Other pages under the home page will share this route area (e.g. /contact-us/).
Essentially just follow the steps in the tutorial but ignore the part about the BlogIndex. Keep your BlogPage model as is and when adding children in the admin UI add them under the home page.
If you want to list all the posts on your HomePage template, you can revise the template context to return blog_pages similar to the documentation.
You can filter a page queryset by type using exact_type. Or, as shown below, you can use BlogPage.childOf(...) to query the other way.
Django documentation about the queryset api.
my-app/models.py
class HomePage(Page):
body = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('body', classname="full"),
]
def get_context(self, request):
context = super().get_context(request)
# Add extra variables and return the updated context
# note: be sure to get the `live` pages so you do not show draft pages
context['blog_pages'] = BlogPage.objects.child_of(self).live()
return context
my-app/templates/home_page.html
{{ page.title }}
{% for blog_page in blog_pages %}
{{ blog_page.title }}
{% endfor %}
Simple, using redirect in urls.py like below code:
from django.views.generic import RedirectView
urlpatterns = [
path(r'^$', RedirectView.as_view(url='/blog/', permanent=False)),
# pass other paths
]
Related
I have a main Django website, which consists of a main page at / using standard django templates, and then a wagtail blog situated at /blog
On my homepage, I would like someone to be able to show the most recent published articles from the wagtail blog.
I'm having a lot of trouble doing this as I can't find any resources or guides.
So far I added a model for my homepage (which didn't have one previously), just to obtain blogs:
from myblog.models import BlogPage
class HomePage(Page):
def blogs(self):
blogs = BlogPage.objects.all()
blogs = blogs.order_by('-date')
return blogs
Then for my homepage template I have:
{% for blog in page.blogs %}
<div class="col-md-4">
<a class="blog-post-link" href="{% pageurl blog %}">
<h3>{{ blog.title }}</h3>
</a>
<div class="blog-intro">
{{ blog.body|richtext|truncatewords_html:50 }}
<a class="read-more" href="{% pageurl blog %}">Read More »</a>
</div>
</div>
{% endfor %}
However nothing is displayed, nor are any errors given.
How can I obtain and display a list of published Wagtail posts in an app outside of Wagtail?
If your homepage is handled by a standard Django view and URL route, then defining a HomePage model will not change that by itself - to go down that path you would also need to:
update your URL routes so that the root path is handled by Wagtail, not just the /blog/ path (i.e. remove the existing route for your homepage view and change path("blog/", include(wagtail_urls)) to path("", include(wagtail_urls)))
create an instance of the HomePage within the Wagtail admin
Adjust the site setup under Settings -> Sites so that the default site points to that homepage as its root page, rather than your blog index page
However, none of this is necessary if you just want to add some Wagtail-derived data to your existing homepage setup - you can just add the BlogPage query to the data that your view function passes to the template. Assuming the view function currently looks something like:
def home(request):
# ... do some processing here ...
return render(request, "home/homepage.html", {
'foo': 'bar',
})
this would become:
def home(request):
# ... do some processing here ...
blogs = BlogPage.objects.all()
blogs = blogs.order_by('-date')
return render(request, "home/homepage.html", {
'foo': 'bar',
'blogs': blogs,
})
You can then access the variable blogs in your template - e.g. {% for blog in blogs %}.
I'm not an expert in Wagtail but did you import wagtail tags in your template?
So I'm in my profile page & want to edit my profile so I click edit button ,it takes me to a edit page where I have two buttons 'save'(inside form tag) & 'Exit'. Now if I click exit I want to get redirected to profile page of same person again. what input should I pass in below tag to do this?
(<button>Exit</button>
adding urls.py
urlpatterns = [
path('',views.home, name='home'),
path('login/',views.loginp, name='login'),
path('profile/<str:ck>/',views.profile, name='profile'),
path('Edit_profile/<str:ck>/',views.Edit, name='Edit'),
path('logout/',views.logoutp, name='logout'),
path('register/',views.register, name='register'),
]
page where my button is located
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<input type ="submit" value="Save">
</form>
<button>Exit</button>
For going back to particular profile you will need a url with profile/int:id and have to pass unique id of that particular profile in your url while calling exit.
Below is the sample you can do it where profile.id contains value of id of a profile.
<button>Exit</button>
i am not agree with other answers.
If you on your profile page, it means you are logged in.
In this case - you don't need to define user.pk, and it is better for usability - i can simply save the link mydomain/profile instead of mydomain/profile/user_pk.
But you should define, how you get an user from Request.
class ProfileView(LoginRequiredMixin, DetailView):
model = User
def get_object(self, *args, **kwargs):
self.kwargs['pk'] = self.request.user.pk
return super().get_object(*args, **kwargs)
I am shure - this is the better solution, than send pk in url.
LoginRequiredMixin- default django mixin for GCBV
DeteilView - django GCBV
More here:
https://docs.djangoproject.com/en/4.1/topics/auth/default/#django.contrib.auth.mixins.LoginRequiredMixin
and
https://docs.djangoproject.com/en/4.1/ref/class-based-views/generic-display/#detailview
I am having issues with get_absolute_url in my django templates when using django page cms.
It returns an empty string and does not link to my required page.
I have the following Models, URLs templates and views
Models
class Body(models.Model):
...
url = models.SlugField(unique=True, help_text='---')
urls
(r'^news/', include('news.urls_news')),......
url(r'^/(?P<url>[\w\-]+)/$', 'news_view', name='news_view'),
View
def news_view(request, url):
new = get_object_or_404(Body, url=url)
return render_to_response('news/view.html', {
'news': news
}, context_instance=RequestContext(request))
Template
<li>{{ news.title }}</li>
the following code in my template returns the string I desire however this does to direct to my html page
<li>{{ news.title }}</li>
I know my everything links to the correct files because I have other views that work correctly. Could somebody please point me in the correct direction as to why get_absolute_url is not working correctly and why {{ news.url }} does not direct to the correct page. I am sure it has something to do with my urls.py however I am not certain.
Please bear with me I am new to django. All help is greatly appreciated.
Have you actually defined a get_absolute_url method on the News model? You don't show it.
I am trying to use comments application in my project.
I tried to use code ({% render_comment_form for event %}), shown in the documentation here:
Django comments
And the question is how to make the form redirect to the same page, after the submission.
Also the big question is:
Currently if we have any error found in the for, then we're redirected to preview template.
Is that possible to avoid this behaviour and display errors over the same form (on the same page)?
I will show you how I resolved it in my blog, so you could do something similar. My comments are for Entry model in entries application.
First add new method for your Entry (like) object.
def get_absolute_url(self):
return "/%i/%i/%i/entry/%i/%s/" % (self.date.year, self.date.month, self.date.day, self.id, self.slug)
It generates url for entry objects. URL example: /2009/12/12/entry/1/lorem-ipsum/
To urls.py add 1 line:
(r'^comments/posted/$', 'smenteks_blog.entries.views.comment_posted'),
So now you should have at least 2 lines for comments in your urls.py file.
(r'^comments/posted/$', 'smenteks_blog.entries.views.comment_posted'),
(r'^comments/', include('django.contrib.comments.urls')),
For entries (like) application in views.py file add function:
from django.contrib.comments import Comment #A
...
def comment_posted(request):
if request.GET['c']:
comment_id = request.GET['c'] #B
comment = Comment.objects.get( pk=comment_id )
entry = Entry.objects.get(id=comment.object_pk) #C
if entry:
return HttpResponseRedirect( entry.get_absolute_url() ) #D
return HttpResponseRedirect( "/" )
A) Import on top of file to have
access for comment object,
B) Get
comment_id form REQUEST,
C) Fetch
entry object,
D) Use
get_absolute_url method to make
proper redirect.
Now:
Post button in comment form on entry site redirects user on the same (entry) site.
Post button on preview site redirects user on the proper (entry) site.
Preview button in comment form on entry site and on preview site redirects user on preview site
Thankyou page is not more in use (That page was quite annoying in my opinion).
Next thing good to do is to override preview.html template:
Go to django framework dir, under linux it could by /usr/share/pyshared/.
Get original preview.html template from DJANGO_DIR/contrib/comments/templates/comments/preview.html
Copy it to templates direcotry in your project PROJECT_DIR/templates/comments/entries_preview.html
From now on, it shoud override default template, You can change extends in this way: {% extends "your_pagelayout.html" %} to have your layout and all css files working.
Take a look at "Django-1.4/django/contrib/comments/templates/comments/" folder and you will see in the "form.html" file, there is the line
{% if next %}<div><input type="hidden" name="next" value="{{ next }}" /></div>{% endif %}
Therefore, in the Article-Detail view, you can include the "next" attribute in the context data, and then the comment framework will do the rest
class ArticleDetailView(DetailView):
model = Article
context_object_name = 'article'
def get_context_data(self, **kwargs):
context = super(ArticleDetailView, self).get_context_data(**kwargs)
context['next'] = reverse('blogs.views.article_detail_view',
kwargs={'pk':self.kwargs['pk'], 'slug': self.kwargs['slug']})
return context
Simplify Django’s Free Comments Redirection
Update: Now have the option to redirect as part of the comment form: see https://django-contrib-comments.readthedocs.io/en/latest/quickstart.html#redirecting-after-the-comment-post
This is a really simple redirect to implement. It redirects you back to the page where the comment was made.
When a comment is posted, the url comments/posted/ calls the view comment_posted which then redirects back to the referer page.
Be sure to replace [app_name] with your application name.
views.py
from urlparse import urlsplit
def comment_posted( request ):
referer = request.META.get('HTTP_REFERER', None)
if referer is None:
pass
try:
redirect_to = urlsplit(referer, 'http', False)[2]
except IndexError:
pass
return HttpResponseRedirect(redirect_to)
urls.py
( r'^comments/posted/$', '[app_name].views.comment_posted' ),
Before Django 1.0 there was an easy way to get the admin url of an object, and I had written a small filter that I'd use like this: <a href="{{ object|admin_url }}" .... > ... </a>
Basically I was using the url reverse function with the view name being 'django.contrib.admin.views.main.change_stage'
reverse( 'django.contrib.admin.views.main.change_stage', args=[app_label, model_name, object_id] )
to get the url.
As you might have guessed, I'm trying to update to the latest version of Django, and this is one of the obstacles I came across, that method for getting the admin url doesn't work anymore.
How can I do this in django 1.0? (or 1.1 for that matter, as I'm trying to update to the latest version in the svn).
You can use the URL resolver directly in a template, there's no need to write your own filter. E.g.
{% url 'admin:index' %}
{% url 'admin:polls_choice_add' %}
{% url 'admin:polls_choice_change' choice.id %}
{% url 'admin:polls_choice_changelist' %}
Ref: Documentation
from django.core.urlresolvers import reverse
def url_to_edit_object(obj):
url = reverse('admin:%s_%s_change' % (obj._meta.app_label, obj._meta.model_name), args=[obj.id] )
return u'Edit %s' % (url, obj.__unicode__())
This is similar to hansen_j's solution except that it uses url namespaces, admin: being the admin's default application namespace.
I had a similar issue where I would try to call reverse('admin_index') and was constantly getting django.core.urlresolvers.NoReverseMatch errors.
Turns out I had the old format admin urls in my urls.py file.
I had this in my urlpatterns:
(r'^admin/(.*)', admin.site.root),
which gets the admin screens working but is the deprecated way of doing it. I needed to change it to this:
(r'^admin/', include(admin.site.urls) ),
Once I did that, all the goodness that was promised in the Reversing Admin URLs docs started working.
Essentially the same as Mike Ramirez's answer, but simpler and closer in stylistics to django standard get_absolute_url method:
from django.urls import reverse
def get_admin_url(self):
return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name),
args=[self.id])
Using template tag admin_urlname:
There's another way for the later versions (>=1.10), recommend by the Django documentation, using the template tag admin_urlname:
{% load admin_urls %}
Add user
Delete this user
Where opts is something like mymodelinstance._meta or MyModelClass._meta
One gotcha is you can't access underscore attributes directly in Django templates (like {{ myinstance._meta }}) so you have to pass the opts object in from the view as template context.
For pre 1.1 django it is simple (for default admin site instance):
reverse('admin_%s_%s_change' % (app_label, model_name), args=(object_id,))
I solved this by changing the expression to:
reverse( 'django-admin', args=["%s/%s/%s/" % (app_label, model_name, object_id)] )
This requires/assumes that the root url conf has a name for the "admin" url handler, mainly that name is "django-admin",
i.e. in the root url conf:
url(r'^admin/(.*)', admin.site.root, name='django-admin'),
It seems to be working, but I'm not sure of its cleanness.
If you are using 1.0, try making a custom templatetag that looks like this:
def adminpageurl(object, link=None):
if link is None:
link = object
return "%s" % (
instance._meta.app_label,
instance._meta.module_name,
instance.id,
link,
)
then just use {% adminpageurl my_object %} in your template (don't forget to load the templatetag first)
For going to the admin page or admin login page we can use the below link. It works for me -
{% url 'admin:index' %}
This url takes me directly to the admin page.
Here's another option, using models:
Create a base model (or just add the admin_link method to a particular model)
class CommonModel(models.Model):
def admin_link(self):
if self.pk:
return mark_safe(u'<a target="_blank" href="../../../%s/%s/%s/">%s</a>' % (self._meta.app_label,
self._meta.object_name.lower(), self.pk, self))
else:
return mark_safe(u'')
class Meta:
abstract = True
Inherit from that base model
class User(CommonModel):
username = models.CharField(max_length=765)
password = models.CharField(max_length=192)
Use it in a template
{{ user.admin_link }}
Or view
user.admin_link()