I want to ask about the use of ._meta in this code ? I didn't find a documentation that explains the use of .meta
def resend_activation_email(self, request, queryset):
"""
Re-sends activation emails for the selected users.
Note that this will *only* send activation emails for users
who are eligible to activate; emails will not be sent to users
whose activation keys have expired or who have already
activated.
"""
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
for profile in queryset:
if not profile.activation_key_expired():
profile.send_activation_email(site)
resend_activation_email.short_description = _("Re-send activation emails")
_meta as the name implies stores the meta data of the model. Say, you want to loop over all the fields of a model, then you can use model._meta.get_fields().
By the way, meta options are covered in the documentation -
Model Meta options documentation
You can also use ._meta in testing.
Consider the below models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
published = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.author} {self.title} {self.content}"
You could use ._meta to verify label names of a post (in test_models.py) like:
from datetime import datetime
from django.test import TestCase
from django.contrib.auth.models import User
from blog.models import Post
class TestBlogModels(TestCase):
def setUp(self):
author = User.objects.create(username='TestUser1')
self.post = Post.objects.create(
author=author,
title='Test Post Title 1',
content='Test content for title 1'
)
....
def test_blog_author_label(self):
author_field = Post._meta.get_field('author').verbose_name
self.assertEqual(author_field, "author")
def test_blog_title_label(self):
title_field = Post._meta.get_field('title').verbose_name
self.assertEqual(title_field, "title")
def test_blog_content_label(self):
content_field = Post._meta.get_field('content').verbose_name
self.assertEqual(content_field, "content")
def test_blog_title_max_length(self):
title_field_length = Post._meta.get_field('title').max_length
self.assertEqual(title_field_length, 100)
...
(verbose_name strips django.db.models.fields to the field speicified in models.py)
Related
I couldn't find a solution to my problem and would appreciate comments/help on this.
I would like to develop a multiple user type model in Django, along the lines of this video where the author is using Django Proxy Models.
Situation
I have a list of XX projects (proj01, proj02 , projXX, ...).
All these projects have their specific page that can be accessed through a specific url mysite/projXX/
I have multiple users: Adam, Bob, Caroline, Dany, Ed, ...
Each user can have several roles according to the project they are working on (e.g. manager, developer, documentarist, reviewer, editor, ...)
A user can have a different role according to the project. E.g. Adam can be reviewer on proj01 but editor on proj02 while Bob can be editor on proj01 but reviewer on proj02, etc..
I started defining multiple user types in the models.py file below (only reviewer and editor roles):
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
class User(AbstractUser):
class Types(models.TextChoices):
EDITOR= "EDITOR", "Editor"
REVIEWER = "REVIEWER", "Reviewer"
base_type = Types.EDITOR
type = models.CharField(
_("Type"), max_length=50, choices=Types.choices, default=base_type
)
name = models.CharField(_("Name of User"), blank=True, max_length=255)
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
def save(self, *args, **kwargs):
if not self.id:
self.type = self.base_type
return super().save(*args, **kwargs)
class EditorManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(type=User.Types.EDITOR)
class ReviewerManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(type=User.Types.REVIEWER)
class EditorMore(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
gadgets = models.TextField()
class Editor(User):
base_type = User.Types.EDITOR
objects = EditorManager()
class Meta:
proxy = True
def edit(self):
return "Edition in progress"
class ReviewerMore(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
model = models.CharField(max_length=255)
make = models.CharField(max_length=255)
year = models.IntegerField()
class Reviewer(User):
base_type = User.Types.REVIEWER
objects = ReviewerManager()
#property
def more(self):
return self.reviewermore
class Meta:
proxy = True
def review(self):
return "In Review"
Question:
What is the best way to handle the fact that the role of the user can change according to the project page he/she is visiting?
Example: If Adam is logged in and visits the page mysite/proj01/ I would like him to access only the content allowed for a reviewer while if Adam visit mysite/proj02/, I would like the user to see only the content allowed to the editor.
Ideally, I would like each user to have its unique entry in the user database. I was thinking that the project-dependent role level could be stored as a dictionary? For example:
{'proj01':'reviewer', 'proj02':'editor', 'projxx': 'roleY', ... }
How would combine this user model and the list of project-dependent permissions?
Edit 02/07/21
Add example files for a project app, models.py and views.py :
# projects/models.py
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse
class Project(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("project_detail", args=[str(self.id)])
# projects/views.py
from django.contrib.auth.mixins import (
LoginRequiredMixin,
UserPassesTestMixin,
)
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, DeleteView, CreateView
from django.urls import reverse_lazy
from .models import Project
class ProjectListView(LoginRequiredMixin, ListView):
model = Project
template_name = "project_list.html"
class ProjectDetailView(LoginRequiredMixin, DetailView):
model = Article
template_name = "project_detail.html"
class ProjectUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Project
fields = (
"title",
"body",
)
template_name = "project_edit.html"
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
If you have projects as a model. You can add custom permissions to the model. Then assign those permissions to your users appropriately for each project (actually easily add/remove permissions too).
Then use either user_passes_test or permissions_required in your views/template to restrict what users can see/access/edit.
class Project(models.Model):
project_name = ...
class RoleType(models.Model):
role_name = models.CharField
# Permission boolean flags
can_edit = models.Boolean
can_view = models.Boolean
class ProjectRole(models.Model):
project = models.ForeignKey('Project', ...)
role = models.ForeignKey('RoleType', ...)
user = models.ForeignKey('User', ...)
Now you can reverse lookup based on project or user
# To show all assigned users and their roles for a project
foo_project = Project.objects.get(project_name='foo')
project_roles = ProjectRole.objects.filter(project=foo_project)
You can also restrict your views and templates by roles and their permissions boolean flags.
Create groups that defines your Roles, for example : Group1: Editor, Group2:Manager and so on
Assign each user to the specified group ( you can do it in python manage.py shell or in admin panel )
Add restrictions on the view, for example: /mysite/projx/ view is restricted to groupA, you can check the following question that helps you with this point: https://stackoverflow.com/a/4789038/13508969
For example:
GroupA : GlobalEditor ( Bob can edit in projx and projy , and can only view projz )
GroupB : viewonly ( Adam can only view the content of the projs )
and so on
I have a model for articles which takes a field FileField which is supposed to be a markdown file for the user to load their article already. I expose the api with a ModelViewSet.
This is saved to my media folder. I could fetch the content from the client side by GETing it from the href of course but that would mean 2 requests to my server:
get article info (title, content- this is the md, date published, description, etc.. ).
get content from the link.
But i'm wondering if there's a way to tell django to just send the content of the file instead of the href when it responds to a requestion for the article item.
Here's my model and api:
# ---------------------- #
# src/articles/models.py #
# ---------------------- #
from os.path import splitext
from uuid import uuid4
from django.db import models
# Create your models here.
def hashFilename(instance, name):
ext = splitext(name)[1]
return "articles/{}{}".format(uuid4(), ext)
def hashImageFilename(instance, name):
ext = splitext(name)[1]
return "images/{}{}".format(uuid4(), ext)
class Article(models.Model):
title = models.CharField(("title"), max_length=100)
content = models.FileField("content", upload_to=hashFilename)
description = models.TextField(("description"), default='')
uploadDate = models.DateTimeField(("uploadDate"), auto_now=True)
lastModified = models.DateTimeField(("uploadDate"), auto_now=True)
publicationDate = models.DateField("publicationDate")
image = models.ImageField("image", upload_to=hashImageFilename)
def __str__(self):
return self.title
# ------------------------- #
# src/articles/api/views.py #
# ------------------------- #
from rest_framework.viewsets import ModelViewSet
from ..models import Article
from .serializers import ArticleSerializerFull, ArticleSerializerShort
class ArticlesViewSet(ModelViewSet):
queryset = Article.objects.all()
def get_serializer_class(self):
if self.action == 'list':
serializer = ArticleSerializerShort
else:
serializer = ArticleSerializerFull
return serializer
queryset = Article.objects.all()
Defining a serializers.SerializerMethodField--(DRF Doc) method will do the job.
class ArticleSerializer(serializers.ModelSerializer):
content = serializers.SerializerMethodField()
def get_content(self, article):
return article.content.file.read()
class Meta:
fields = '__all__'
model = Article
Alternatively, you could achieve the same by overriding the to_representation method of the serializer.
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Article
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['content'] = instance.content.file.read()
return rep
Update-1
From this comment, I hope you need a live markdown editor in Django Admin rather than a FileField.
So, Use any of these markdown packages to get a live view in the Django Admin. These packages are using models.TextField to store the markdown content. So that you could read the content from the field in anywhere just like any other model fields
Django and MongoDB
Supporting a different set of fields for each document in a collection is one of MongoDB's features. It allows you to store similar data, but with different properties in the same collection.
for example:
{
_id: ObjectId("51156a1e056d6f966f268f81"),
type: "Article",
author: "Derick Rethans",
title: "Introduction to Document Databases with MongoDB",
date: ISODate("2013-04-24T16:26:31.911Z"),
body: "This artiā¦"
},
{
_id: ObjectId("51156a1e056d6f966f268f82"),
type: "Book",
author: "Derick Rethans",
title: "php|architect's Guide to Date and Time Programming with PHP",
isbn: "978-0-9738621-5-7"
}
Django dose not support Non-Relational data base like mongodb by default, but there are some lib's for this purpose. for example Django MongoDB Engine is a MongoDB backend for Django.
MongoDB allow to use different set of fields for each document in a collection, but in django you have to define models.py:
from django.db import models
from djangotoolbox.fields import ListField
class Post(models.Model):
title = models.CharField()
text = models.TextField()
tags = ListField()
comments = ListField()
the Question is: is there any way to define different set of fields for each document in a collection in MongoDB, when using Django ?
The Alternative
I like using django-mongoengine as it makes things clearer when dealing with MongoDB models.
For example, you can create structured Documents that are going to be transformed into models or EmbeddedDocument`s that are structured documents to be used in an already existed model.
from django_mongoengine import Document, EmbeddedDocument, fields
class Comment(EmbeddedDocument):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
author = fields.StringField(verbose_name="Name", max_length=255)
email = fields.EmailField(verbose_name="Email")
body = fields.StringField(verbose_name="Comment")
class Post(Document):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
title = fields.StringField(max_length=255)
slug = fields.StringField(max_length=255, primary_key=True)
comments = fields.ListField(
fields.EmbeddedDocumentField('Comment'), blank=True,
)
The Answer
So for your case what you need to use is Dynamic document schemas that work in the same way as Document but any data/attributes set to them will also be saved.
class Page(DynamicDocument):
title = StringField(max_length=200, required=True)
# Create a new page and add tags
>>> page = Page(title='Using MongoEngine')
>>> page.tags = ['mongodb', 'mongoengine']
>>> page.save()
>>> Page.objects(tags='mongoengine').count()
>>> 1
I struggled this problem in user profile page. This is my solution.
model.py
from django.contrib.auth.models import User
from django.db import models
from django.core.validators import RegexValidator
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,related_name="user_profile")
fullname = models.CharField(max_length=100,verbose_name="full name")
about = models.CharField(max_length=300,blank=True,null=True)
hobies = models.CharField(max_length=200,blank=True)
recent_aktivity = models.CharField(max_length=150,verbose_name="recent activity",null=True)
photo = models.ImageField(blank=True,null=True,upload_to="images/")
recent_badges = models.CharField(max_length=100,verbose_name="recent badges",null=True)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=True,null=True,verbose_name="phone number")
website_url = models.URLField(blank=True,null=True,verbose_name="company website")
projects =models.CharField(max_length=200,blank=True,null=True)
bio = models.CharField(max_length=300,blank=True,null=True)
def __str__(self):
return f'{self.user.username}-ProfileModel'
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile
#receiver(post_save,sender=User)
def update_user_profile(sender,instance,created,**kwargs):
if created:
profile = Profile.objects.create(user =instance)
app.py
from django.apps import AppConfig
class ProfileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Profile'
def ready(self):
import Profile.signals
forms.py
from django import forms
from.models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = '__all__'
exclude = ['user']
views.py
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.urls.base import reverse
from .forms import ProfileForm
from .models import Profile
from django.shortcuts import redirect, render,get_object_or_404
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
login_required(login_url="user:login")
def dashboard(request):
return render(request,"dashboard.html")
#login_required(login_url="user:login")
def get_profile(request):
profile = get_object_or_404(Profile,user=request.user)
return render(request,"profile.html",{"profile":profile})
I have written a custom save method in my Django model class link.
I have added this method so that if i use admin panel to enter data it will get executed and i didn't have to enter data manually for that field but now when i enter data from admin panel it still asks me to enter the data but then it simply overrides my entry.Is Their any way it will not allow to enter that particular value from admin panel.
import uuid
import base64
import datetime
from django.db import models
from django.contrib import admin
#------------------------------------------------------------------------------
def _generateShortUrl():
"""
This function will generate base64 encoded id
"""
return base64.urlsafe_b64encode(uuid.uuid1().bytes)[:6]
class link(models.Model):
"""
This is a link class
"""
link = models.URLField() # To store user entered URL
hits = models.IntegerField(default=0) # How many hits for particular URL
last_used = models.DateTimeField(auto_now=True) # When URL is last used
short_url = models.CharField(max_length=6,unique=True) # base64 URL encoded id
def linkValidate(self):
timediff = datetime.datetime.now() - self.last_used
return timediff.min
def save(self, *args, **kwargs):
"""
Custom Save method for link model
"""
self.short_url = _generateShortUrl()
super(link, self).save(*args, **kwargs)
class user_info(models.Model):
"""
This is a user_info class
"""
user_agent = models.TextField() # Stores user_agent name used by user
user_ip = models.ManyToManyField(link) # Stores user's IP
def userIP(self):
"""Method to return tags related to store"""
return ','.join([t.link for t in self.link.all()])
#------------------------------------------------------------------------------
class linkAdmin(admin.ModelAdmin):
"""
link admin class
"""
list_display = ('link','hits','short_url','last_used',
'linkValidate')
ordering = ('hits',)
class userInfoAdmin(admin.ModelAdmin):
"""
user_info admin class
"""
list_display = ('user_agent','userIP')
#------------------------------------------------------------------------------
admin.site.register(link,linkAdmin)
admin.site.register(user_info,userInfoAdmin)
You need to tell your Model & ModelAdmin not to require those fields.
https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#django.contrib.admin.ModelAdmin.exclude
You can change you field definition to:
short_url = models.CharField(max_length=6,unique=True, default=_generateShortUrl)
or change you linkAdmin adding :
exclude = ('short_url',)
You should try assigning a value to shortURL when the instance is created rather then when stored.
I have a model, configuration, in Django and wish to fill the author field with get_username
Can this be done within the model or must it be done from the form? If it must be on the form, how can I change the standard admin page to have this functionality?
At present, the model reads thus:
class Configuration(models.Model):
title = models.CharField(max_length=100,unique=True,blank=False)
author = models.CharField(max_length=50,blank=False)
created = models.DateTimeField("date created",auto_now_add=True)
modified = models.DateTimeField("date modified",auto_now=True)
description = models.CharField(max_length=512)
drawing = models.ForeignKey(Drawing)
instruments = models.ManyToManyField(Instrument)
def __unicode__(self):
return self.title
Use models.ForeignKey:
#models.py
from django.contrib.auth.models import User
class Configuration(models.Model):
author = models.ForeignKey(User)
...
#admin.py:
class Configuration_admin(admin.ModelAdmin):
fields = ('title', 'author',....)
something like that:
from django.contrib.auth.models import User
class ...
...
username = models.ForeignKey(User)
If you want to make some relationship between your model and default User model then you can extends the User model into your own custom model , like this:
models.py
from django.contrib.auth.models import User
class Configuration(models.Model):
author = models.OneToOneField(User)
..
..