rendered slugs are not correct in url django - python

Why do I always have an extra %2F in my generated slugs urls?
All the slugs are generating correctly below when I print them out in the terminal, but I don't know why the url has the extra %2F
Something is wrong somewhere but I cannot seem to spot it
In my view i am using <a href = "{{ group.get_absolute_url }}">... to get the slug. Now, this works but outputs the above problem. If i do href = "{% url 'group' group.slug %} this throws an error that it can't find a reverse match.
Example: title of group is a group the url will be ../%2Fgroup/a-group/
in urls.py
(r'^/group/(?P<slug>[-\w\d]+)/$', "group"),
model
class BlogGroup(models.Model):
title = BleachField()
image = models.ImageField(upload_to="uploads/group_images", default="uploads/group_images/none/none.jpg")
created = models.DateTimeField(default = datetime.now)
slug = models.SlugField(unique = True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("blog.views.group", kwargs = {'slug':self.slug})
form
class BlogGroupForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BlogGroupForm, self).__init__(*args, **kwargs)
self.fields["title"].requried = True
self.fields["image"].required = True
self.fields["title"].widget = forms.TextInput()
class Meta:
model = BlogGroup
fields = ["title", "image", "slug"]
def save(self, commit = False):
instance = super(BlogGroupForm, self).save(commit = False)
return truncate_slug(instance, BlogGroup)
utils.py
from django.utils.text import slugify
import itertools
def truncate_slug(instance, arg):
length = arg._meta.get_field('slug').max_length
instance.slug = original_slug = slugify(instance.title)[:length]
for x in itertools.count(1):
if not arg.objects.filter(slug = instance.slug).exists():
break
instance.slug = "%s-%d" % (original_slug[:length - len(str(x)) -1], x)
instance.save()
return instance

You have a forward slash at the beginning of your regex. If you remove it, it should prevent the %2f (note that %2f is a url encoded forward slash).
url(r'^group/(?P<slug>[-\w]+)/$', "group", name="group"),
Note that I have also
removed \d, since \w already includes digits 0-9
used url() (best practice for Django 1.8+) and named the url pattern. That should hopefully make reversing the url with the {% url %} tag work. Using group.get_absolute_url in your template is fine though, there's no need to use the url tag if you don't want to.

Related

How can I pass a model id from the url to a class based view?

I have a class-based view:
class Create(View):
note_id = None
http_method_names = ['post', 'patch']
default_title = "You fool! This was left empty"
default_body = "Why did you leave this blank :("
def dispatch(self, *args, **kwargs):
method = self.request.POST.get('_method', '').lower()
print('method = ', method)
if method == 'patch':
return self.patch(*args, **kwargs)
elif method == 'post':
self.post(*args, **kwargs)
return super(Create, self).dispatch(*args, **kwargs)
def post(self, note_id):
date = datetime.date.today()
title = self.request.POST.get('title', '')
body = self.request.POST.get('note', '')
# check for blank attributes
if title == "":
title = Create.default_title
if body == "":
body = Create.default_body
note = Note(note_title=title, note_body=body, publish_date=date, edit_date=None)
note.save()
return HttpResponseRedirect(reverse('notes:note'))
def patch(self, note_id):
note = Note.objects.get(id=note_id)
title = self.request.POST.get('title', '')
body = self.request.POST.get('note', '')
# if something changed
if title != note.note_title or body != note.note_body:
# check for blank attributes
if title == "":
title = Create.default_title
if body == "":
body = Create.default_body
note.note_title = title
note.note_body = body
note.edit_date = datetime.date.today()
note.save()
return HttpResponseRedirect(reverse('notes:note'))
and in url.py I have
urlpatterns = [
path('<int:note_id>/create/', views.Create.as_view(), name='create'),
path('<int:note_id>/edit/', views.Create.as_view(), name='edit')
]
Previously, with function-based views the note_id would just be passed to the function automatically. I can not figure out the equivalent of this in class based views. I've tried explictiely passing note_id to each function, but that did not work. I tried included Create.as_view(note_id=note_id) in my url.py, but to no avail.
Currently, running this code I get:
post() got multiple values for argument 'note_id'
As mentioned in the comment above, you need to access these values through self.kwargs. e.g:
class Create(View):
note_id = None # Delete this line
...
# delete the dispatch method - no need to overwrite this.
# Don't include note_id as an argument, but request should be an argument
def post(self, request):
note_id = self.kwargs['note_id']
....
def put(self, request): # Likewise here
note_id = self.kwargs['note_id']
....
It doesn't look like you need to write a custom dispatch method. If all you are doing is calling the appropriate method, then the dispatch method that comes for free with View is just fine :)

Django admin site change_list view customization

I have a ModelAdmin subclass for my gradeScalesSettings model:
#admin.register(gradeScalesSetting)
class gradeScalesSettingAdmin(admin.ModelAdmin):
list_display = ('configuration_select', 'NumberOfGrades', 'Rounding','Precision', 'Status',)
change_list_template = 'admin/Homepage/view.html'
Actual result
After I click Grade Scale Settings:
How to connect it to my views.py?
This is what I want to code in my views.py:
def gradescales(request):
gradeScalesSettings = gradeScalesSetting.objects.all()
configurations = configuration.objects.all()
rounding = gradeScalesSetting.objects.all().values_list('Rounding', flat=True).distinct()
print(rounding)
return render(request, 'Homepage/gradescale.html', {"rounding": rounding,"gradeScalesSetting":gradeScalesSettings,"configurations":configurations})
When I tried this:
#admin.register(gradeScalesSetting)
class gradeScalesSettingAdmin(admin.ModelAdmin):
def new_NumberOfGrades(self, obj):
if obj.NumberOfGrades == 'Grade Scale Settings':
return '<a href="view.html" </a>' # this url will redirect to your
In my ModelAdmin subclass:
list_display = ('configuration_select', 'new_NumberOfGrades', 'Rounding','Precision', 'Status',)
Is there any way to connect it to my views.py?
Expected result
This is what I want to show in my view.html:
That is why I want to connect it to my views.py.
Override ModelAdmin.changelist_view to set extra_context.
#admin.register(gradeScalesSetting)
class gradeScalesSettingAdmin(admin.ModelAdmin):
list_display = ('configuration_select', 'NumberOfGrades', 'Rounding','Precision', 'Status',)
change_list_template = 'admin/Homepage/view.html'
def changelist_view(self, request, extra_context=None):
extra_context = extra_context or {}
gradeScalesSettings = gradeScalesSetting.objects.all()
configurations = configuration.objects.all()
rounding = gradeScalesSetting.objects.all().values_list('Rounding', flat=True).distinct()
extra_context.update({
"rounding": rounding,
"gradeScalesSetting": gradeScalesSettings,
"configurations": configurations,
})
return super().changelist_view(request, extra_context=extra_context)
You can alter routing for ModelAdmin by overriding get_urls:
class gradeScalesSettingAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super().get_urls()
pat = [i for i in urls if str(i.name).endswith('changelist')][0] # Find needed path object to replace
index = urls.index(pat)
urls[index] = path(pat.pattern._route, gradescales)
return urls
This way you can have full control of a view used for your admin page and even add additional pages (more than only changelist and edit views) as you want.
If you do not need additional default views like object edit page, you can simplify code above by replacing original urls instead of searching one needed and patching:
class gradeScalesSettingAdmin(admin.ModelAdmin):
def get_urls(self):
urls = [
path('', gradescales)
]
return urls

Django form field update

I have a Django form that lets users choose a tag from multiple tag options. The problem I am facing is that even when the tag list gets updated, the model form does not get the updated tag list from database. As a result, new tags do not appear in options.
Here is my code in forms.py:
class EnglishTagForm(forms.Form):
tag_choices = [(x.tagName, x.tagName.upper()) for x in ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury'))]
tag = forms.CharField(widget=forms.Select(choices=tag_choices,
attrs={'class':'form-control'}))
def __init__(self, *args, **kwargs):
super(EnglishTagForm, self).__init__(*args, **kwargs)
self.fields['tag'].choices = [(x.tagName,
x.tagName.upper()) for x in ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury'))]
This form is being instantiated in view. My question is what changes should I do so that tag_choices gets updated from database on every instantiation.
How the above form is used in views.py:
```
def complaintDetail(request, complaint_id):
complaint = Complaints.objects.filter(pk=complaint_id).first()
context = {}
if request.method == 'POST':
agent = Agent.objects.get(name="English Chowdhury")
if "SubmitTag" in request.POST:
englishForm = EnglishTagForm(request.POST)
if englishForm.is_valid:
// Complaint Delete Logic
return redirect('chatbot:modComplaints')
else:
englishForm = EnglishTagForm()
context['eForm'] = englishForm
elif "SubmitBundle" in request.POST:
newTagForm = NewTagForm(request.POST)
if newTagForm.is_valid():
// Complaint Delete Logic
complaint.delete()
return redirect('chatbot:modComplaints')
else:
newTagForm = NewTagForm()
context['newForm'] = newTagForm
else:
englishForm = EnglishTagForm()
context['eForm'] = englishForm
newTagForm = NewTagForm()
context['newForm'] = newTagForm
context['complaint'] = complaint
return render(request, 'chatbot/complaintDetail.html', context)
```
Edit: (For future reference)
I decided to modify the tag attribute and convert CharField to ModelChoiceField, which seems to fix the issue.
Updated Class:
class EnglishTagForm(forms.Form):
tag = forms.ModelChoiceField(queryset=ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury')),
empty_label=None, widget=forms.Select(
attrs={'class':'form-control'}))
Please remove the list comprehension from Line 2. So that,
tag_choices = [(x.tagName, x.tagName.upper()) for x in ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury'))]
becomes
tag_choices = []

django-bootstrap3 Parameter "field" should contain a valid Django BoundField?

I am using django-bootstrap3 to render my forms on the template and i have been struggling to find what is causing the error Parameter "field" should contain a valid Django BoundField when i try to load the page with the form on it. I have attached my code and error below. can someone please point for me what i'm doing wrong?
forms.py
class OrderForm(forms.Form):
first_name = forms.CharField(max_length=50)
last_name = forms.CharField(max_length=50)
email = forms. EmailField(max_length=50)
institution_name = forms.CharField(max_length=150)
phone = forms.IntegerField()
address = forms.CharField(max_length=100)
city = forms.CharField(max_length=50)
item = forms.CharField(max_length=100)
serial_number = forms.CharField(max_length=50)
problem = forms.CharField(widget=forms.Textarea(attrs—Crows':10,'cols':18,'style':'resize:none', [placeholder':'Please define your problem here'l),label='Problem description')
[placeholder':'Please define your problem here'l),label='Problem description')
views.py
def Orderview(request):
if request.method == 'Post':
order_form = OrderForm(request.POST)
if order_form.is_valid():
cd = order form.cleaned data
subject = '{} repair order from {}'.format(cd['item'],cd['institution_name'])
from_email = cd['email']
to = [settings.EMAIL_HOST_USER,]
ctx = {
'first_name':cd['first_name'],
'last_name':cd['last_name'],
'email':cd['email'],
'institution_name':cd['institution_name'],
'phone':cd['phone'],
'address':cd['address'],
'city':cd['city'],
'item':cd['item'],
'serial_number.:cd['serial_number'],
'problem':cd['problem'],
}
message = get_template('electroapp/email/order.html').render(Context(ctx))
msg = EmailMessage(subject,message,to=to,from_email=from_email)
msg.content_subtype='html'
msg.send()
messages.success(request,' Your Repair order has been sent',)
return redirect('electroapp:repair_order')
else:
order_form = OrderForm()
return render(request,'electroapp/orderform.html',{'Order_form':order_form})
template
browser error
console logs
This could be because some fields may be missing.Take a look at this
You could do something like this to see what fields are available:
<form role="form" method="post">
{% csrf_token %}
{% bootstrap_form order_form %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>
and then try to figure out why you are having missing fields.
Parameter "field" should contain a valid Django BoundField
Why can't they just tell us which actual field? Well, here's how I solved it.
A little debugging and you'll find this is thrown within renderers.py:
class FieldRenderer(BaseRenderer):
"""Default field renderer."""
# These widgets will not be wrapped in a form-control class
WIDGETS_NO_FORM_CONTROL = (CheckboxInput, RadioSelect, CheckboxSelectMultiple, FileInput)
def __init__(self, field, *args, **kwargs):
if not isinstance(field, BoundField):
raise BootstrapError('Parameter "field" should contain a valid Django BoundField.')
self.field = field
super().__init__(*args, **kwargs) ...
The message is not helpful, but can be fixed in your dev/test environment.
WARNING: The following will void your warranty! :)
Go up a few steps in the render chain in library.py (for example ~/.virtualenvs/your_env/lib/python3.8/site/packages/django/template/library.py)
and find SimpleNode.
class SimpleNode(TagHelperNode):
def __init__(self, func, takes_context, args, kwargs, target_var):
super().__init__(func, takes_context, args, kwargs)
self.target_var = target_var
def render(self, context):
resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
output = self.func(*resolved_args, **resolved_kwargs)
if self.target_var is not None:
context[self.target_var] = output
return ''
if context.autoescape:
output = conditional_escape(output)
return output
After copying the original file to a backup, change it as follows. We are wrapping the line
output = self.func(*resolved_args, **resolved_kwargs)
with a try/except. Here's the result:
class SimpleNode(TagHelperNode):
def __init__(self, func, takes_context, args, kwargs, target_var):
super().__init__(func, takes_context, args, kwargs)
self.target_var = target_var
def render(self, context):
resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
###
from bootstrap3.exceptions import BootstrapError
try:
output = self.func(*resolved_args, **resolved_kwargs)
except BootstrapError as b:
raise BootstrapError(str(b).replace('"field"', self.args[0].token)) from b
###
if self.target_var is not None:
context[self.target_var] = output
return ''
if context.autoescape:
output = conditional_escape(output)
return output
Within the SimpleNode class we have access to the name of the field that couldn't be rendered. So we intercept the exception there and re-throw it with the useful tidbit injected. Voila, you will now get an exception report that tells you the actual problem field.
Cheers.

Using tastypie resource in view

my first question here :
So I'm using tastypie to have api's for my app.
I want to be able to use tastypie to render json and then include that in a django view so that I can bootstrap my app's data.
There is an example of this in django tastypie cookbook here : http://django-tastypie.readthedocs.org/en/latest/cookbook.html#using-your-resource-in-regular-views
The problem is that I CANNOT get this to work, I've tried variants from simpler to more complex and I just cant get it, here some code for my models :
class ChatMessage(models.Model):
content = models.TextField()
added = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(ChatUser, related_name="messages")
chat_session = models.ForeignKey(ChatSession, related_name="messages")
answer_to = models.ForeignKey('self', blank=True, null=True)
flagged = models.BooleanField(blank=True,default=False)
mododeleted = models.BooleanField(blank=True,default=False)
mododeleted_by = models.ForeignKey(ChatUser,blank=True,null=True,default=None)
mododeleted_at = models.DateTimeField(blank=True,null=True,default=None)
[...]
class ChatSession (models.Model):
title = models.CharField(max_length=200)
link_title = models.CharField(max_length=200)
description = tinymce_models.HTMLField()
date = models.DateTimeField()
online = models.BooleanField(default=False)
next_session = models.BooleanField(default=False)
meps = models.ManyToManyField(ChatMep)
uid_newsupdate = models.CharField(max_length=200,blank=True,null=True,default="")
[...]
and my resources :
class ChatMessageResource(MyModelResource):
chat_session = fields.ForeignKey(ChatSessionResource, 'chat_session')
def renderOne(self,request,pkval):
data = self.obj_get(None,pk=pkval)
dbundle = self.build_bundle(obj=data,request=request)
return self.serialize(None,self.full_dehydrate(dbundle),'application/json')
def dehydrate(self, bundle):
bundle.data['likes'] = bundle.obj.get_likes()
bundle.data['likes_count'] = len(bundle.data['likes'])
return bundle
class Meta:
authentication = Authentication()
authorization = Authorization()
queryset = ChatMessage.objects.all()
resource_name = 'message'
fields = ('content', 'added', 'flagged', 'mododeleted','author','answer_to','chat_session')
filtering = {
'chat_session': ALL_WITH_RELATIONS,
}
and my view index :
def index(request):
cur_sess = get_current_chat_session()
data1= ChatMessageResource().renderOne(request,723)
return render_to_response('test.html',
{
'all_data' : data1
},
context_instance=RequestContext(request))
What I want is my renderOne() function to give me the json of ONE ChatMessageResource
And also I'd like a renderAll() function to gice me ALL (or filtered) ChatMessageResources in json.
And I want to use tastypie internals, I KNOW i could serialize it by myself but that's not the point..
Right now the error is :
NoReverseMatch at /live/
Reverse for 'api_dispatch_detail' with arguments '()' and keyword arguments '{'pk': 14L, 'resource_name': 'session'}' not found.
I'm just getting crazy, I've been trying for hours.
So please, how to get ONE/ALL resource as JSON by code using tastypie in a django view !
If It's not clear or I need to clarify, please just ask, thanks
Really what I want to do is to be able to get the JSON returned by an API url I created, but from code, not by visiting the url .. So If I have /api/v1/messages/?chat_session=14 which return a list of messages, I want to be able to do the same by code (and not by fetching the url with curl or something please).
Note :
definition of ModelResource.obj_get from https://github.com/toastdriven/django-tastypie/blob/master/tastypie/resources.py
def obj_get(self, request=None, **kwargs):
"""
A ORM-specific implementation of ``obj_get``.
Takes optional ``kwargs``, which are used to narrow the query to find
the instance.
"""
try:
base_object_list = self.get_object_list(request).filter(**kwargs)
object_list = self.apply_authorization_limits(request, base_object_list)
stringified_kwargs = ', '.join(["%s=%s" % (k, v) for k, v in kwargs.items()])
if len(object_list) <= 0:
raise self._meta.object_class.DoesNotExist("Couldn't find an instance of '%s' which matched '%s'." % (self._meta.object_class.__name__, stringified_kwargs))
elif len(object_list) > 1:
raise MultipleObjectsReturned("More than '%s' matched '%s'." % (self._meta.object_class.__name__, stringified_kwargs))
return object_list[0]
except ValueError:
raise NotFound("Invalid resource lookup data provided (mismatched type).")
So here I found the solution, the problem was with url resolving ... I needed to add
def get_resource_uri(self, bundle_or_obj):
return '/api/v1/%s/%s/' % (self._meta.resource_name,bundle_or_obj.obj.id)
to the related object (session here) in order for it to work (don't ask why!)
So here is my working solution for renderDetail and renderList :
def renderDetail(self,pkval):
request = HttpRequest()
request.GET = {'format': 'json'}
resp = self.get_detail(request, pk=pkval)
return resp.content
def renderList(self,options={}):
request = HttpRequest()
request.GET = {'format': 'json'}
if len(options) > 0:
request.GET.update(options)
resp = self.get_list(request)
return resp.content
And here is an example usage :
cmr = ChatMessageResource()
dataOne= cmr.renderDetail("723")
dataAll = cmr.renderList({'limit':'0','chat_session':cur_sess.pk})
https://github.com/toastdriven/django-tastypie/issues/962
I've found that obj_get method needs a bundled request object. See the link.
def user_detail(request, username):
ur = UserResource()
# Add this request bundle to the obj_get() method as shown.
req_bundle = ur.build_bundle(request=request)
user = ur.obj_get(req_bundle, username=username)
....
Your problem seems to be here:
data = self.obj_get(None,pk=pkval)
The parameters to obj_get should be kwargs that can be passed directly to a standard get. None should not be in there.

Categories

Resources