Provide a default profile picture if ever empty - django

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.

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.

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

Django custom url structure

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 }}

Djangothumbnails app issue

I an using this plugin to generate thumbnails. But somhow I couldn't make it work. The models work well, as images can be uploaded from the admin interface, even thumbnails get generated. I uploaded an image named "myphoto.jpg". The view I have is this.
def mainpage(request,slug):
page = get_object_or_404(MainPage, slug=slug)
image = get_object_or_404(Image)
return direct_to_template(request, 'flatpage.html',
extra_context={
'page':page,
'image':image,
})
I have defined the class in models as "Image". I had this in the template:
<img src="{% image.photo.myphoto_125x125 %}"> But the template shows error.
This is my model:
class Image(models.Model):
title = models.CharField(max_length=100)
photo = ImageWithThumbsField(upload_to='images/', sizes=((125,125),(200,200)))
# second_photo = ImageWithThumbsField(upload_to='site_media/images')
def __unicode__(self):
return self.title
The second field, "second_photo" I have commented out as it created duplicate copy of the main image. Even I am looking for a way to create entry for actual unsized image in the first field itself.
I havent tried this app, but I am using PIL to create thumbnail versions of uploaded images, and using a def in the model to retrieve the path to the thumbnail for display. This may be more of a manual approach than you are looking for but it works ok for me. You can override the save method on your model (which has your FileField) or send a post_save signal to a def to create the thumbnail.
But curious to know if this app you are using adds some useful functionality over this manual approach.
Somehow I got it working. Instead of creating a separate class, I used this line photo = ImageWithThumbsField(blank=True, upload_to='images/', sizes=((125,125),(200,200))) in my blog class. And I used the line {{% post.photo.url_125x124 %} in my index.html template.

Why is my Django modelformset always invalid when i use a custom model manager?

After stripping down my code to the minimum, it still does not work. I alway get the hint:
(Hidden field id) Select a valid choice. That choice is not one of the available choices.
This is what my forms looks like:
class ChangeItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ChangeItemForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
class Meta:
model = Item
fields = ('name','tags','no')
ChangeItemFormset=modelformset_factory(Item,extra=0,form=ChangeItemForm)
and my view looks like this:
def manage_view(request):
if request.method=='POST':
itemforms=ChangeItemFormset(request.POST,
queryset=Item.objects.filter(creator=request.user))
else:
itemforms=ChangeItemFormset(queryset=Item.objects.filter(creator=request.user))
messages.info(request,str(itemforms.is_valid())) #always prints False
context=RequestContext(request)
context.update({'formset':itemforms,
})
return render_to_response('sonitem/test_forms.html',context)
and in my template i do this:
<form action="." method="post" name="upload_image">
<button type="submit" name="action" value="change">change</button>
{%for form in formset.forms%}
{{form}}
{%endfor%}
{{formset.management_form}}
{%csrf_token%}
</form>
messages:
{%for message in messages%}
<div>{{message}}</div>
{%endfor%}
Thats it. I don't have a clue where to look further. Maybe i do it all wrong? At least i am sure i somehow missed an important piece of how formsets work... if someone could please help me out.
edit:
looks like it's somehow related to my model Item. Just made a new, simplified model, Item2, and this code worked exactly as it should. Item2 had just the fields that are in the form above. Back to Item: Why (and more important - how) can modelfields that are not in the formset affect the formset validation?
The Item2 model, the one that is working:
class Item2(models.Model):
name=models.CharField(max_length=50)
tags=TagField()
no=models.IntegerField(blank=True,null=True)
creator = models.ForeignKey(User, related_name='creator')
edit2:
I think i have encircled what causes the trouble: i have defined a model manager as default, that is looking for a status - which is a models.IntegerField. As soon as i put this into the class, it stops working and delivers exactly the error message from above.
The Item model looks somewhat like this:
class Item(models.Model):
PRIVATE_STATUS=1
PUBLIC_STATUS=2
RELEASED_STATUS=3
STATUS_CHOICES=((PRIVATE_STATUS ,'private'),
(PUBLIC_STATUS ,'public' ),
(RELEASED_STATUS,'released'))
status = models.IntegerField(choices=STATUS_CHOICES,default=PRIVATE_STATUS)
public = PublicItemManager()
objects = models.Manager()
name=models.CharField(max_length=50)
tags=TagField()
no=models.IntegerField(blank=True,null=True)
file=models.FileField()
creator = models.ForeignKey(User, related_name='creator')
status=models.IntegerField(blank=True,null=True)
So i have to extend my question above. Is it possible to use the status (which definitely is a required field) in the model and still use a formset? The formset is only for editing, not for creating the items. And it is always prepopulated, there is no chance there will be an empty status-field.
I still don't understand how a field that is not even in the form can impede the validation of it. And, by the way, if i am using just the ChangeItemForm, it does not.
edit 3:
here is the manager, stripped down to the most simple version causing trouble:
class PublicItemManager(models.Manager):
def get_query_set(self):
return super(PublicItemManager,
self).get_query_set().filter(status=self.model.PUBLIC_STATUS)
when i
#public=PublicItemManager
everything runs smooth.
edit 4:
oh, and by the way: Why is the public manager affecting any validation, when i have the queryset working with the objects manager?
queryset=Item.**objects**.filter(creator=request.user)
After studying the django-docs for quite a while i was able to find the solution. Looks like in certain situations django creates "automatic" managers that are not the _default_manager. Docs are here: http://docs.djangoproject.com/en/1.2/topics/db/managers/#controlling-automatic-manager-types
And here is the code for the working Manager:
class PublicItemManager(models.Manager):
#this is the important line:
use_for_related_fields = True
def get_query_set(self):
return super(PublicItemManager,self).get_query_set().filter(status=self.model.PUBLIC_STATUS)