Django custom url structure - django

Is there any built-in or 3rd party app for custom styled urls?
I'd like to have custom urls such as:
example.com/article-type/article-name-2015-24-12
Where article-type would be a created from a foreign key and at the end of the url there would be the published date.
And in templates I'd call them using {% url 'article' article.pk %}. (So in the future when my boss decides to change the url structure I don't have to change it everywhere)
Is there anything like that? If not could anyone kick me to the right direction how to implement a such feature?

You could build the URL on the model, overriding the get_absolute_url() method.
Something like this
def Something(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField()
created = models.DateTimeField()
article_type = models.ForeignKey(ArticleTypes)
def get_absolute_url(self):
return '/{}/{}-{}-{}-{}'.format(
self.article_type, self.slug, self.created.year,
self.created.month, self.created.day)
And in the template, you'd use
{{ something.name }}

Related

Django global variable based on user groups

In my Django project I have a database that is populated from outside Django, but needs the Django functionality to display data in the database in a user friendly manner. The legacy database structure is as follows:
class SomeModel(models.Model):
#some fields
group_access = models.ForeignKey(AccessGroup,on_delete=models.CASCADE()
class AccessGroup(models.Model):
group_name = models.CharField(max_length=200)
The users have a custom User profile with manytomany relationship with group names assigned to the specific user:
class CustomUser(models.Model):
#some values
projects = models.ManyToManyField(AccessGroup)
Currently I am able to display data from all groups a user has access to, but what I am looking for is a way to create a drop down menu so that users can switch between groups without the need to log out or reenter group on every view.
You could try something like this:
AccessGroup.objects.filter(CustomUser__pk=1)
Or
CustomUser.objects.filter(AccessGroup__group_name='GropName')
https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_many/
you can extend the django user model, somthing like
from django.contrib.auth.models import User
class CustomUser(models.Model):
projects = models.ManyToManyField(AccessGroup)
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
custom_user = models.ForeignKey(CustomUser, unique=False)
class SomeModel(models.Model):
#some fields
group_access = models.ForeignKey(AccessGroup,on_delete=models.CASCADE()
class AccessGroup(models.Model):
group_name = models.CharField(max_length=200)
then something like this to get the data in your view
def index(request):
AccessGroup.objects.filter(user__id=persion(request.user).id)
I'll assume you know how to get the list of groups, and are just looking as to how to get this list into templates. If not, let me know and I'll explain that as well.
If you're trying to get a global variable into templates, there are really 3 main options:
Make a custom template tag that takes the current user as input, and generates this list as output.
Use Middleware to generate the list, and append it to the current context for each request
Use a method on your user class, or a mixin of it (really easy if you use a custom user class), and just call that method as user.method in your templates. Remember to exclude parentheses from the method call (only in templates), and keep in mind that this method shouldn't accept any parameters other than self.
Thank you everybody for getting me on the right track. What I ended up doing is writing a context processor for checking the user permissions:
#context_processors.py
def check_groups(request):
group_check = AccessGroup.objects.values('id','group_name').filter(projects=request.user.id)
return {
'group_check': group_check,
}
Afterwards I created a Bootstrap-select dropdown in my base.html
<select class="selecpicker">
<optgroup>
<option data-hidden="true">Choose group</option>
{% for grpup in group_check %}
<option val="group.id">{{ group.group_name }}</option>
{% endfor %}
</optgroup>
And the it is just a matter of users using it as means to switch access groups in views and passing the value via ajax to any other template views I come across.
Not the 100% what I was looking for, but it works and my users are happy.

Getting values from django model for further use in view

I have a model with 3 fields (name, url, date). How can I get url in my view and put it inside method?
sites = Site.objects.all().order_by('date')[:3]
I need to get host from url (http://www.example.com to example.com). I have a class for this task:
class SiteThumb():
def get_site_thumb(self, url):
host = urlparse(url).hostname
if host.startswith('www.'):
host = host[4:]
return(host)
I would like to use it later in my template (something similar to {{ site.url }} but with use of variable 'host' related to particular 'site' object).I can not deal with it. I can with another view where is only one site:
def site(request, category_slug, subcategory_slug, id):
There is id which I can simply assign to particular object. In my base view
def index(request):
I have to load a couple of objects. Thanks for any clues.
One approach is model methods.
For eg. Site is a model in which you have the said three fields url, name and date. Now you can define a model method for Site model which you can access in template. It can be done in this way.
class Site(models.Model):
url = field
name = field
date = field
def hostname(self):
#It is copy paste of your implementation
host = urlparse(url).hostname
if host.startswith('www.'):
host = host[4:]
return host
In your template on every Site instance this method can be accessed as {{ site.hostname }}.

How to display multiple forms of a single model in Django templates?

I have this model Note:
class Note(models.Model):
category = models.ForeignKey(Category)
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=40)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
And I want to display this form:
class NoteEditForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title', 'text')
in a template, but I want it to appear for each existing Note object in the database (it has to be that way). I've done something like that but then I hardcoded the form, pointing to the edit view URL with each object pk as a parameter; but I'm sure it has to be a clearer way, just I haven't found it. Could you guys help me with that? Thanks!
The easiest way to do this is to use a formset (also see model formsets).
For your Note model and NoteEditForm you could do something like this. You'd usually put this wherever you've defined your NoteEditForm but it can go in another file, such as views.py.
from django.forms import modelformset_factory
NoteEditFormSet = modelformset_factory(Note, form=NoteEditForm)
Using NoteEditFormSet in a view and template is almost the same as using a regular form, but there are a few differences to be aware of if you want to do anything complicated so have a look at the docs (view and template). If that's not clear enough, add a few details of what you're trying to do in your view and template and I'll try to help.
By default the formset will use Note.objects.all() as its queryset, which is what you say you want, but you can change that (details are covered in the docs).
Update:
To save an individual Note with an AJAX request I would add a second view to handle those requests. So if your formset for all Notes is served by a view at /notes/, you could add a view to handle your AJAX request at /notes/<note_id>/ (obviously just an example, adjust to fit your URL structure).
Then your JS on the /notes/ page is responsible for serializing the data for a single note and making the request to /notes/<note_id>/ (remember the CSRF token).
The HTML inputs generated for the formset have their IDs prefixed with something like id_form-<number>- and there are hidden inputs containing Note primary keys which will let you work out which ID prefix applies to each note.
I would think about doing it like this
{% for note in Notequeryset %}
<form action={% url 'url_name_to_form' pk={{note.pk}} %}>
{{form.as_p}}
</form>
{% endfor %}
Let me know what you think

How to use Django build-in comment framework on any template

I just begin to study Django build-in comments function. at first I think the comment template should work well on any page just with get_comment_form or render_comment_form .but now it really annoying when i add these code to a ordinary page. It doesn't work. maybe in a other word. I don't know how to specify the object to attached to when it come to a normal page. below is the detail message :
models.py
class Entry(models.Model):
title = models.CharField(max_length=250)
body = models.TextField()
pub_date = models.DateTimeField()
enable_comments = models.BooleanField()
urls.py
urlpatterns = patterns('',
url(r'^profile/','django.views.generic.simple.direct_to_template',{
'template' : 'admin_ryu_blog/profile.html'},name='profile'),
)
now i just want to using comment framework on template profile.html. what should i do ? you could regard profile.html as a blank page now. then you can add any code you want if you can get a comment form displayed with build-in comment framework.
btw I have tried the below method :
{% load comments %}
{% render_comment_form for profile %}
then it prompt a error message. the same traceback on my previous question .
click here!
You can't. The comments framework expects an object to reference.
But an easy solution that comes to mind is building a model that maps to URLs like so:
class CommentAnchor(models.Model):
path = models.CharField(max_length=256)
Build a context processor that builds these objects and adds them to all template context . Remember to add your context processor to your settings.TEMPLATE_CONTEXT_PROCESSORS, and remember to use RequestContext when rendering templates.
def CommentAnchorProcessor(request):
comment_anchor, created = CommentAnchor.objects.get_or_create(path=request.path)
return {
'comment_anchor': comment_anchor, # now, this is available in every template.
}
Now you can render comments via these new objects.
{% render_comment_form for comment_anchor %}

Provide a default profile picture if ever empty

I would like to provide a default profile picture for a user. I've used the default feature on the ImageField, but it acts more like an 'initial' than a default. I've read over the answers to this question: Default image for ImageField in Django's ORM, but I'd like to solve the problem at the model level and it does not address with FK relationships.
Here is what I currently have:
class Avatar(models.Model):
avatar = models.ImageField(upload_to=upload_to)
avatar_thumbnail = models.ImageField(upload_to=upload_to)
profile = models.ForeignKey(UserProfile)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
...
I've tried adding the following function to the UserProfile model (and more than likely, incorrect code) --
# in UserProfile(models.Model)
def create_default_avatar(self):
if not self.avatar_set.all():
avatar = profile.avatar_set.create(profile=self.__class__)
avatar.avatar = File(open('media/default_profile_picture.jpg'))
avatar.avatar_thumbnail = File(open('media/default_profile_picture_thumbnail.jpg'))
avatar.save()
Could someone please show me how to create a true default picture for a profile. Thank you.
Ignacio's answer is simple but not perfect. It breaks the DRY principle. You have to add in all your templates the {% if image %} statements. I suggest creating a property on on the model, which will handle that for you (in all places at once).
class Avatar(models.Model):
(...)
#property
def avatar_image_url(self):
# Pseudocode:
if has image:
return url to the image
else:
return url to default image
Then in template you just do:
<img src="{{ item.avatar_image_url }}" alt="..."/>
Store nothing in the field if the default is to be used, and use the {% if %} template tag to detect this.