在 Django 中向所有用户公开主题

Posted

技术标签:

【中文标题】在 Django 中向所有用户公开主题【英文标题】:Making a topic public to all users in Django 【发布时间】:2020-09-24 22:58:45 【问题描述】:

我有一个项目,用户可以在其中输入主题并选择将主题设为公开或私有。我希望所有人都可以看到所有公共主题,并且只有该主题的所有者才能看到私有主题(项目来自 Python 速成课程练习 20-5)。 当我点击我的项目时,我没有收到任何错误,但是当我创建一个新主题并选择“公开”选项时,它仍然没有显示给公众查看。

我研究过类似的问题,并按照这里的所有建议 How to make a topic public to all users in django? 和这里 Django - How to make the topics, that you create public? Learning Log Project 没有运气。

我猜我的查询集没有正确呈现数据?我也阅读了有关查询集的 Django 文档,但我尝试过的任何方法都没有奏效。我对编程真的很陌生,所以任何帮助都将不胜感激。谢谢!

这是我的models.py

from django.db import models
from django.contrib.auth.models import User

# Create your models here.

class Topic(models.Model):
    """A topic the user is learning about"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    public = models.BooleanField(default=False)

    def __str__(self):
        """Return a string representation of the model"""
        return self.text    

class Entry(models.Model):
    """Something specific learned about a topic"""
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
    text = models.TextField()
    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = 'entries'

    def __str__(self):
        """Retrun a string representation of the model"""
        if len(self.text) < 50:
            return f"self.text"
        else:
            return f"self.text[:50]..."

views.py:

from django.shortcuts import render, redirect, get_object_or_404 
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.http import Http404

from .models import Topic, Entry
from .forms import TopicForm, EntryForm




# Create your views here.
def index(request):
    """The home page for Learning Log"""
    return render(request, 'learning_logs/index.html')


#Checks whether logged in user has access to topic
def check_topic_owner(request, topic):
    if topic.owner != request.user:
        raise Http404

def topics(request):
    """Shows all topics"""
    #Gets public topics
    public_topics = Topic.objects.filter(public=True).order_by('date_added')
    #Gets private topics
    if request.user.is_authenticated:
        private_topics = Topic.objects.filter(owner=request.user).order_by('date_added')
        topics = public_topics | private_topics
    else: 
        topics = public_topics

    context = 'topics': topics
    return render(request, 'learning_logs/topics.html', context)


def topic(request, topic_id):
    """Show a single topic and all its entries"""
    topic = get_object_or_404(Topic, id=topic_id)

    entries = topic.entry_set.order_by('-date_added')
    context = 'topic': topic, 'entries': entries
    return render(request, 'learning_logs/topic.html', context)

@login_required
def new_topic(request):
    """Add a new topic"""
    if request.method != 'POST':
        #No data submitted; create a blank form.
        form = TopicForm()
    else:
        #POST data submitted; process data
        form = TopicForm(data=request.POST)
        if form.is_valid():
            new_topic = form.save(commit=False)
            new_topic.owner = request.user
            new_topic.save()
            return redirect('learning_logs:topics')

    #Display a blank or invalid form
    context = 'form': form
    return render(request, 'learning_logs/new_topic.html', context)

@login_required
def new_entry(request, topic_id):
    """Add a new entry for a particular topic"""
    topic = get_object_or_404(Topic, id=topic_id)
    check_topic_owner(request, topic)

    if request.method != 'POST':
        #No data submitted, create a blank form
        form = EntryForm()
    else:
        #POST data submitted; process data
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.topic = topic
            if new_entry.topic.owner == request.user:
                new_entry.save()
            else:
                return Http404
            return redirect('learning_logs:topic', topic_id=topic_id)

    #Display a blank or invalid form
    context = 'topic': topic, 'form': form
    return render(request, 'learning_logs/new_entry.html', context)

@login_required
def edit_entry(request, entry_id):
    """Edit an existing entry"""
    entry = get_object_or_404(Entry, id=entry_id)
    topic = entry.topic
    check_topic_owner(request, topic)

    if request.method != 'POST':
        #Initial request; prefill with the current entry
        form = EntryForm(instance=entry)
    else:
        #POST data submitted; process data
        form = EntryForm(instance=entry, data=request.POST)
        if form.is_valid():
            form.save()
            return redirect('learning_logs:topic', topic_id=topic.id)

    context = 'entry': entry, 'topic': topic, 'form': form
    return render(request, 'learning_logs/edit_entry.html', context) 

new_topic.html:

% load bootstrap4 %

% block page_header %
    <h3>Add a new topic:</h3>
% endblock page_header %

% block content %

    <form method="post" action="% url 'learning_logs:new_topic' %" class="form">
        % csrf_token %
        % bootstrap_form form %
        <div class="form-check">
            <input class="form-check-input" type="checkbox" value=True id="public" />
            <label class="form-check-label" for="public">
                <p>Make post public?<p/>
            </label>

        </div>
        % buttons %
            <button name="submit" class="btn btn-primary">Add topic</button>
        % endbuttons %
    </form>

% endblock content %

还有forms.py:

from .models import Topic, Entry

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text']
        labels = 'text': ''

class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ['text']
        labels = 'text': ''
        widgets = 'text': forms.Textarea(attrs='cols': 80)

还有topics.html:


% block page_header %
    <h1>Topics</h1>
% endblock page_header %

% block content  %
    <ul>
        % for topic in topics %
            <li><h4>
                <a href="%url 'learning_logs:topic' topic.id %"> topic </a>
            </h4></li>

        % empty %
            <li><h4>No topics have been added yet.</h4></li>
        % endfor %
    </ul>

    <h4><a href="% url 'learning_logs:new_topic' %">Add a new topic</a></h4>

    % endblock content %

编辑:

在 hasnain095 的帮助下,我现在已经让项目按预期工作了。这是我更新的new_topic.html。我仍然不明白复选框是如何生成的,因为我已经删除了我认为专门生成它的 html,但由于它仍在工作,我很满意。

% extends "learning_logs/base.html" %
% load bootstrap4 %

% block page_header %
    <h3>Add a new topic:</h3>

% endblock page_header %

% block content %

    <form method="post" action="% url 'learning_logs:new_topic' %" class="form">
        % csrf_token %
        % bootstrap_form form %

        % buttons %
            <button name="submit" class="btn btn-primary">Add topic</button>
        % endbuttons %
    </form>

% endblock content %

【问题讨论】:

能否提供topics.html 好的,刚刚做了。谢谢! 您在两次查询之间执行了|,但只能通过Q 操作完成 【参考方案1】:

我认为问题在于您只在主题表单中指定了“文本”字段:

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text']
        labels = 'text': ''

但是在new_topic.html文件中,你只有一个复选框输入,没有地方输入文本

<div class="form-check">
            <input class="form-check-input" type="checkbox" value=True id="public" />
            <label class="form-check-label" for="public">
                <p>Make post public?<p/>
            </label>

        </div>
        % buttons %
            <button name="submit" class="btn btn-primary">Add topic</button>
        % endbuttons %

您的“将帖子公开?”复选框正在映射到您的主题的“文本”字段。

解决方法:

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text', 'public']
        labels = 'text': ''

<div class="form-check">
            <input class="form-check-input" type="checkbox" value=True id="public" />
            <label class="form-check-label" for="public">
                <p>Make post public?<p/>
            </label>

            <input class="form-input" type="input" value=True id="text1" />
            <label class="form-label" for="text1">
                <p>Text<p/>
            </label>


        </div>

【讨论】:

将“公共”标签添加到我的 TopicForm 类中的字段就可以了!我实际上需要完全删除整个 div 类,因为这会创建额外的复选框。我有由
创建的文本字段。我实际上不知道现在复选框是如何生成的。但不知何故,这一切都在正常工作。谢谢!
我没有使用过 Django 模板,所以我也不确定。很高兴我能帮上忙。【参考方案2】:

使用联合添加到查询集:

def topics(request):
    """Shows all topics"""
    # Gets public topics (renamed to topic_set to not shadow function name)
    topic_set = Topic.objects.filter(public=True).order_by('date_added')
    # Add private topics, if any. We save some work by not including the public
    # topics.
    if request.user.is_authenticated:
        topic_set = topic_set.union(
            Topic.objects.filter(public=False, owner=request.user)
        )

    context = 'topics': topic_set
    return render(request, 'learning_logs/topics.html', context)

旁注:为您过滤的字段添加索引:

class Topic(models.Model):
    """A topic the user is learning about"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    public = models.BooleanField(default=False, db_index=True)

    def __str__(self):
        """Return a string representation of the model"""
        return self.text    

【讨论】:

现在这隐藏了我所有的主题,即使是以前由用户拥有的可见的主题。【参考方案3】:

使用 union 将私有主题附加到公共主题后,它对我有用,但可能有一些不同。下面列出来,希望对您有所帮助: 视图.py

def check_topic_owner(topic, request):
if not topic.public:
    if topic.owner != request.user:
        raise Http404   


def topics(request):
#Gets public topics
public_topics = Topic.objects.filter(public=True)
#Gets private topics and append the public topic
if request.user.is_authenticated:
    private_topics = Topic.objects.filter(owner=request.user)
    topics = public_topics.union(private_topics).order_by('date_added')
else: 
    topics = public_topics


context = 'topics': topics
return render(request, 'learning_logs/topics.html', context)

models.py

class Topic(models.Model):

text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
public = models.BooleanField(default=False)

def __str__(self):
    return self.text

【讨论】:

以上是关于在 Django 中向所有用户公开主题的主要内容,如果未能解决你的问题,请参考以下文章

Django 1.4 - 在元字段 Model=User 的表单中向用户字段添加自定义错误消息

如何在 NiFi 中向管理员用户授予所有权限

如何在 Django 中向 ModelForm 添加外键字段?

Django 并在不同视图中向外部 API 发布请求

在 MySQL 中向新用户授予权限 [重复]

在 PHP 中向特定用户推送通知