django : url change depends on model exist - django

I'd like to make user access to page depends on model exist. and I use CBV.
Do I have to control views? or urls?
Is FBV only way to control url?
How can I control user access url? hope kindly help me.
i'd like to control for example:(as you know, this is invalid syntax. hope you know what i'm saying.)
from django.urls import path
from . import views, models
app_name = "papers"
urlpatterns = [
path(
"memberagreement/<int:preassociation_pk>/",
{% if models.MemberAgreement.association.get(pk=preassociaion_pk) is not NULL %}
views.member_agreement_detail_vc.as_view(),
{% else %}
views.member_agreement_create_vc.as_view(),
{% endif %}
name="member_agreement_vc",
)
]
I add my views.py:(it works when models is exist, but if model does not exist, i can't load my template...)
def member_agreement_vc(request, preassociation_pk):
preassociation = preassociation_models.Preassociation.objects.get(
pk=preassociation_pk
)
try:
member_agreement = models.MemberAgreement.objects.get(pk=1)
return render(
request,
"papers/member_agreement/detail.html",
{"member_agreement": member_agreement},
)
except models.MemberAgreement.DoesNotExist:
form_class = forms.CreateMemberAgreementFormVC
template_name = "papers/member_agreement/create.html"
def form_valid(self, form):
pk = self.kwargs.get("preassociation_pk")
member_agreement = form.save()
# content
# association
# writer
# participants
# category
# is_business
# is_general
# number_of_investment_account
# name
# resident_registration_number
# address
# contact
member_agreement.writer = self.request.user
member_agreement.association = preassociation_models.Preassociation.objects.get(
pk=pk
)
member_agreement.category = "member_agreement"
member_agreement.is_business = True
member_agreement.is_general = False
member_agreement.save()
form.save()
return redirect(
reverse(
"preassociations:paper",
kwargs={"pk": member_agreement.association.pk},
)
)

What you are asking, I think is not possible. URL is just a path, or a way you will do nothing in there except walking. Just like you are new to a road, you will just walk, then when you have to decide where to go, either you will ask google to do so, or the nearby person. So, road is like url and google map is like views where you will decide where to go.
I am going to try it the views ways then,
// urls.py
// Add your path
// views.py
from .models import YourModel // import model
def decideWhereToGo(request):
modelExist = YourModel.objects.filter(someField=someValue).exists()
if modelExist:
// Do sthg
else:
// redirect to the url where you want to send if model does not exists

Related

django redirect to different view/page if permission denied

Hej!
I have a view where contact details are rendered. The goal is to have some users who are allowed to see all details ('A') and some who only can see for example the name ('B').
Therefore I have two serializers in my model and two views (one for each serializer). The users are divided in two groups.
The #user_passes_test does work so only the one in group 'A' are able to get into the view with the details.
I want the ones which are in group 'B' to be redirected automatically to the view/page where the details are minimized.
Any idea how to achieve that?
I've got:
# views.py
def contacts_check(user):
return user.groups.filter(name="Contacts = True").exists()
#login_required
#user_passes_test(contacts_check)
def contacts(request):
.
.
.
#login_required
def contacts_small(request):
.
.
.
# urls.py
app_name = "app"
urlpatterns = [
path("contacts", contacts, name="contacts"),
path("contacts2", contacts_small, name='contacts2')
]
I tried:
#user_passes_test(contacts_check, login_url='contacts')
but both user groups are directed to the url 'contacts' with the full view. ('B' gets an error that the user has no access). Same when login_url='contacts2'.
I also tried:
def sees_contacts(self):
if str(self.user.groups.filter) == 'Contacts = True':
return True
else:
return False
con_login_required = user_passes_test(lambda u: True if u.sees_individuals else False, login_url='individuals2')
def contacts_login_required(view_func):
decorated_view_func = login_required(con_login_required(view_func), login_url='contacts2')
return decorated_view_func
not filtering/not redirection properly.
# views.py
def contacts_check(user):
return user.groups.filter(name="Contacts= True").exists()
def contacts_view(request):
if contacts_check():
return search_contacts(request)
return search_contacts_small(request)
#login_required
def search_contacts(request):
....
#login_required
def search_contacts_small(request):
....
Does anyone has an idea what I'm doing wrong?
In urls.py just use one path
path("contacts/", contacts_view, name="contacts-view"),
then in the method
def contacts_view(request):
if sees_contacts():
return contacts(request)
return contacts_small(request)
This will help keep url patterns less confusing for you and users and allow you to easily separate the views.

why django model can't save my new object when i click create button in 'create.html'

I'm beginning to use Django, and I have some problems. I want to create new post such as blog. So I use views.py with model.py and forms.py.
but when I enter the create.html, I was writing what i want to post, and then click 'create' button. but it wasn't save in django object. I check in admin site, but there is no object. I think it means save object is failed. but I don't know where is the problem. plz help me T.T
in views.py
def create(request):
if request.method =="POST":
filled_form = ObForm(request.POST)
if filled_form.is_valid():
filled_form.save()
return redirect('index')
Ob_form = ObForm()
return render(request, 'create.html', {'Ob_form':Ob_form})
in create.html
<body>
<!-- form.py 모델 생성 -->
<form method="POST" action="">
{% csrf_token %}}
{{Ob_form.as_p}}
<input type="submit" value="확인" />
</form>
</body>
in models.py
from django.db import models
class Ob(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(null=True)
content = models.TextField(null=True)
update_at = models.DateTimeField(auto_now=True)
in forms.py
from django import forms
from .models import Ob
# 모델폼을 상속받아서 모델폼이 되었음
class ObForm(forms.ModelForm):
# 어떤 모델과 대응되는지 말해줌
class Meta:
model = Ob
fields = ( "title", "image", "content")
# 모델 폼 커스텀
# init - 내장함수 - (해당 클레스에 들어오는 여러가지 인자를 받을 수 있는 파라미터)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "제목"
self.fields['image'].label = "사진"
self.fields['content'].label = "자기소개서 내용"
self.fields['title'].widget.attrs.update({
'class': 'Ob_title',
'placeholder': '제목',
})
and urls.py
from django.urls import path
from .views import index, create, detail, delete, update
urlpatterns = [
path('', index, name="index"),
path('create/', create, name="create"),
path('detail/<int:Ob_id>', detail, name="detail"),
path('delete/<int:Ob_id>', delete, name="delete"),
path('update/<int:Ob_id>', update, name="update"),
]
A bit of guesswork here, but it's probably the case that filled_form.is_valid() is returning false, which will mean save() is never reached. To test this simply, just put an else and print on the if.
if filled_form.is_valid():
filled_form.save()
return redirect('index')
else:
print("Form validation failed")
print(filled_form.errors)
It's likely that you'd also want to return these errors to the user in the future, which means you'll need to make a couple of changes.
Right now, regardless of whether ObForm validates successfully, you are creating a new instance and passing that to the user. My typical approach would be to declare the form variable at the top of the function, and if it isn't already set when it comes to render the view, create a new instance of the form then. This way, if the form was populated by the user already (i.e. the request was a POST) then the errors will be returned with their input, instead of clearing their input (which is really annoying from a user's point of view!).
As a side note, I'm going to guess that you submitted a form with empty image and content fields, and were expecting that to be stored in your database? Try changing your field declarations to:
title = models.CharField(max_length=50)
image = models.ImageField(null=True, blank=True)
content = models.TextField(null=True, blank=True)
update_at = models.DateTimeField(auto_now=True)
null=True tells the database to allow null values, but blank=True tells the form validation to allow empty values.

Adding a button to Wagtail Dashboard

Is it possible to add a additional button(buttons) on the top panel as shown in the picture?
I did't find anything in Google and here.
It is not clear on the screenshot whether you use modeladmin or the snippets to expose this model to the user but I'll assume the former.
I don't know about a hook which allows you to add buttons directly to the header, however the template uses blocks which should allow us to overwrite just this part.
We can take advantage of the resolution order of the templates and create templates/modeladmin/app-name/model-name/index.html which will take precedence over /modeladmin/index.html. So given with your app called feedler and a model called Entry, create /modeladmin/feedler/entry/index.html with the following content:
{% extends "modeladmin/index.html" %}
{% block header_extra %}
My New Button
{{ block.super }}{% comment %}Display the original buttons {% endcomment %}
{% endblock %}
Right now your button doesn't do much. To create an action which will interact with that model admin, you'll need to create some button/url/permission helpers and a view.
Let's say the action is exporting the objects to a CSV file. Brace yourself, there's quite a bit of code ahead.
In /feedler/admin.py, create the button/url/permission helpers and view:
from django.contrib.auth.decorators import login_required
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from wagtail.contrib.modeladmin.helpers import AdminURLHelper, ButtonHelper
from wagtail.contrib.modeladmin.views import IndexView
class ExportButtonHelper(ButtonHelper):
"""
This helper constructs all the necessary attributes to create a button.
There is a lot of boilerplate just for the classnames to be right :(
"""
export_button_classnames = ['icon', 'icon-download']
def export_button(self, classnames_add=None, classnames_exclude=None):
if classnames_add is None:
classnames_add = []
if classnames_exclude is None:
classnames_exclude = []
classnames = self.export_button_classnames + classnames_add
cn = self.finalise_classname(classnames, classnames_exclude)
text = _('Export {}'.format(self.verbose_name_plural.title()))
return {
'url': self.url_helper.get_action_url('export', query_params=self.request.GET),
'label': text,
'classname': cn,
'title': text,
}
class ExportAdminURLHelper(AdminURLHelper):
"""
This helper constructs the different urls.
This is mostly just to overwrite the default behaviour
which consider any action other than 'create', 'choose_parent' and 'index'
as `object specific` and will try to add the object PK to the url
which is not what we want for the `export` option.
In addition, it appends the filters to the action.
"""
non_object_specific_actions = ('create', 'choose_parent', 'index', 'export')
def get_action_url(self, action, *args, **kwargs):
query_params = kwargs.pop('query_params', None)
url_name = self.get_action_url_name(action)
if action in self.non_object_specific_actions:
url = reverse(url_name)
else:
url = reverse(url_name, args=args, kwargs=kwargs)
if query_params:
url += '?{params}'.format(params=query_params.urlencode())
return url
def get_action_url_pattern(self, action):
if action in self.non_object_specific_actions:
return self._get_action_url_pattern(action)
return self._get_object_specific_action_url_pattern(action)
class ExportView(IndexView):
"""
A Class Based View which will generate
"""
def export_csv(self):
data = self.queryset.all()
response = ...
return response
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
super().dispatch(request, *args, **kwargs)
return self.export_csv()
class ExportModelAdminMixin(object):
"""
A mixin to add to your model admin which hooks the different helpers, the view
and register the new urls.
"""
button_helper_class = ExportButtonHelper
url_helper_class = ExportAdminURLHelper
export_view_class = ExportView
def get_admin_urls_for_registration(self):
urls = super().get_admin_urls_for_registration()
urls += (
url(
self.url_helper.get_action_url_pattern('export'),
self.export_view,
name=self.url_helper.get_action_url_name('export')
),
)
return urls
def export_view(self, request):
kwargs = {'model_admin': self}
view_class = self.export_view_class
return view_class.as_view(**kwargs)(request)
In /feedler/wagtail_hooks.py, create and register the ModelAdmin:
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .admin import ExportModelAdminMixin
from .models import Entry
class EntryModelAdmin(ExportModelAdminMixin, ModelAdmin):
model = Entry
# ...
modeladmin_register(EntryModelAdmin)
With all that setup, you should be able to use {% include 'modeladmin/includes/button.html' with button=view.button_helper.export_button %} in the template created above.
Watch out, if you are listing Pages you will need to subclass from PageAdminURLHelper, not from AdminURLHelper (FilterableAdminURLHelper doesn't seem to be a standard implementation).
Otherwise, you will have some strange redirections taking place.

Tango With Django - Fun With Forms URLconf Error

I am currently doing the latest tango with django course and have just about finished '8. Fun with Forms', I however cannot solve an error I am getting. The course tells us to make an add_page form and supplies us with the view and the form, all we really have to do is create the URL, I have done this however I cannot get it to recognize the URL for the add_page view.
Sorry I cannot post pictures as I do not have high enough reputation so I have given links.
This is the error I get
http://i.stack.imgur.com/UrFxv.png
Here is my code
URLS -
from django.conf.urls import patterns, url
from rango import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^about/', views.about, name="about"),
url(r'^add_category/$', views.add_category, name='add_category'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/add_page/$', views.add_page, name='add_page'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category'),
)
VIEWS -
from django.shortcuts import render
from django.http import HttpResponse
from rango.models import Category, Page
from rango.forms import CategoryForm, PageForm
def index(request):
# Query the database for a list of ALL categories currently stored.
# Order the categories by no. likes in descending order.
# Retrieve the top 5 only - or all if less than 5.
# Place the list in our context_dict dictionary which will be passed to the template engine.
category_list = Category.objects.order_by('-likes')[:5]
page_list = Page.objects.order_by('-views')[:5]
context_dict = {'categories': category_list, 'pages': page_list}
# Render the response and send it back!
return render(request, 'rango/index.html', context_dict)
def about(request):
message = "Rango says here is the about page. <a href='/rango/'>Index</a> "
return HttpResponse(message)
def category(request, category_name_slug):
# Create a context dictionary which we can pass to the template rendering engine.
context_dict = {'category_name_slug': category_name_slug}
try:
# Can we find a category name slug with the given name?
# If we can't, the .get() method raises a DoesNotExist exception.
# So the .get() method returns one model instance or raises an exception.
category = Category.objects.get(slug=category_name_slug)
context_dict['category_name'] = category.name
# Retrieve all of the associated pages.
# Note that filter returns >= 1 model instance.
pages = Page.objects.filter(category=category)
# Adds our results list to the template context under name pages.
context_dict['pages'] = pages
# We also add the category object from the database to the context dictionary.
# We'll use this in the template to verify that the category exists.
context_dict['category'] = category
except Category.DoesNotExist:
# We get here if we didn't find the specified category.
# Don't do anything - the template displays the "no category" message for us.
pass
# Go render the response and return it to the client.
return render(request, 'rango/category.html', context_dict)
def add_category(request):
# A HTTP POST?
if request.method == 'POST':
form = CategoryForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
# Save the new category to the database.
form.save(commit=True)
# Now call the index() view.
# The user will be shown the homepage.
return index(request)
else:
# The supplied form contained errors - just print them to the terminal.
print form.errors
else:
# If the request was not a POST, display the form to enter details.
form = CategoryForm()
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render(request, 'rango/add_category.html', {'form': form})
def add_page(request, category_name_slug):
try:
cat = Category.objects.get(slug=category_name_slug)
except Category.DoesNotExist:
cat = None
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
if cat:
page = form.save(commit=False)
page.category = cat
page.views = 0
page.save()
# probably better to use a redirect here.
return category(request, category_name_slug)
else:
print form.errors
else:
form = PageForm()
# made the change here
context_dict = {'form': form, 'category': cat, 'category_name_slug': category_name_slug}
return render(request, 'rango/add_page.html', context_dict)
FORMS -
from django import forms
from rango.models import Page, Category
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128, help_text="Please enter the category name.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
# An inline class to provide additional information on the form.
class Meta:
# Provide an association between the ModelForm and a model
model = Category
fields = ('name',)
class PageForm(forms.ModelForm):
title = forms.CharField(max_length=128, help_text="Please enter the title of the page.")
url = forms.URLField(max_length=200, help_text="Please enter the URL of the page.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
class Meta:
# Provide an association between the ModelForm and a model
model = Page
# What fields do we want to include in our form?
# This way we don't need every field in the model present.
# Some fields may allow NULL values, so we may not want to include them...
# Here, we are hiding the foreign key.
# we can either exclude the category field from the form,
exclude = ('category',)
#or specify the fields to include (i.e. not include the category field)
#fields = ('title', 'url', 'views')
def clean(self):
cleaned_data = self.cleaned_data
url = cleaned_data.get('url')
# If url is not empty and doesn't start with 'http://', prepend 'http://'.
if url and not url.startswith('http://'):
url = 'http://' + url
cleaned_data['url'] = url
return cleaned_data
I think that is all the code necessary however just let me know if there are another files you would like to see and I'll put them up. Any help appreciated, thanks
A simple mistake, in my browser I was doing this 'http://127.0.0.1:8000/rango/category/other-frameworks/add_page.html', however my URL Tuple was not looking for a '.html' as the end so I removed it so it looked like this 'http://127.0.0.1:8000/rango/category/other-frameworks/add_page' and that solved my problem. Thanks to sgmart.

Download Files in Django

I need some help understanding how to download files from my django site to a users machine. I have already created a fairly basic way to upload the files. The files are then an instance of a class and are saved in my "media" folder. What I need help with is how to serve the files. I want the user to be able to visit a page and click on the files they want to download. For some reason I can't find any resources on how to do this. Here are my files at this point
urls.py
url(r'^admin/', include(admin.site.urls)),
url(r'^upload/', views.upload),
url(r'^download/', views.download),
url(r'^success/', views.success),
)
if settings.DEBUG:
urlpatterns = patterns('',
url(r'^media/(?P<path>.*)$', 'django.views.static.serve',{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
) + urlpatterns
models.py
class WorkSheet(models.Model):
worksheet_name = models.CharField(max_length= 150, default = '')
creator = models.ForeignKey(User, default = True)
worksheet_file = models.FileField(upload_to = 'worksheets', default = True)
number_of_stars = models.PositiveIntegerField(default = 0)
category = models.CharField(max_length = 100, default = '')
class UploadWorkSheetForm(ModelForm):
class Meta:
model = WorkSheet
exclude = ('number_of_stars',
'creator',)
views.py
def upload(request):
if request.method == 'POST':
form = UploadWorkSheetForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return http.HttpResponseRedirect('/success/')
else:
form = UploadWorkSheetForm()
return render(request, 'upload.html', {'form': form})
def download(request):
return render_to_response('download.html')
def success(request):
return render_to_response('upload_success.html')
So basically I want the user to visite www.mysite.com/download and be able to download a file. Thank you!!
.
.
.
.
.
.
.
.
.
Also, is it a problem that my upload file view does not have a handler like this?
def handle_uploaded_file(file,path=''):
filename = file._get_name()
destination_file = open('%s/%s' % (settings.MEDIA_ROOT, str(path) + str(filename)), 'wb+')
for chunk in file.chunks():
destination_file.write(chunk)
destination_file.close()
In your download view, you are just rendering a download.html, but you are not sending any contexts to it. I would may be send a queryset of worksheets,(Worksheet.objects.all()) as a context to the template. And in the template, do something like
{% for worksheet in worksheets %}
{{ worksheet.worksheet_file.url }}
{% endfor %}
Then you would have all the file urls present in your WorkSheet.objects.all() query.
If possible I would handle all the upload logic in the models file itself, something like this