Django多对一关系实现:用户技能列表关联客户问题求助
Hey there! Let's break down what's going wrong and fix this step by step. Your core issue is misunderstanding how ForeignKey relationships work in Django, plus needing to handle splitting the text input into individual skills.
You placed the ForeignKey on the Post model, which means each Post can only be linked to one Skill—the opposite of what you need (one client with multiple skills). The correct structure is to attach the ForeignKey to the Skill model, so one Post can have many Skills:
# models.py from django.db import models from django.utils import timezone from django.contrib.auth.models import User from django.urls import reverse class Skill(models.Model): element = models.TextField() # Remove null=True unless you want empty skills post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='skills') # related_name lets you access a post's skills with `post.skills` later def __str__(self): return self.element class Post(models.Model): name = models.CharField(max_length=100) # Remove null=True unless empty names are allowed description = models.TextField(null=True, blank=True) date_posted = models.DateTimeField(default=timezone.now) author = models.ForeignKey(User, on_delete=models.CASCADE) def __str__(self): return self.name def get_absolute_url(self): return reverse('post-detail', kwargs={'pk': self.pk})
Since the Post model no longer has a skills field, we'll add a temporary text field to a custom form to accept the user's newline-separated skill list:
# forms.py from django import forms from .models import Post class PostCreateForm(forms.ModelForm): # Temporary field for user's skill list input skills = forms.CharField(widget=forms.Textarea(attrs={'rows': 4}), required=False) class Meta: model = Post fields = ['name', 'description', 'skills']
Rewrite the form_valid method in your view to first save the Post, then split the input text and create individual Skill instances linked to the new client:
# views.py from django.views.generic.edit import CreateView from django.contrib.auth.mixins import LoginRequiredMixin from .models import Post, Skill from .forms import PostCreateForm class PostCreateView(LoginRequiredMixin, CreateView): model = Post form_class = PostCreateForm # Use our custom form instead of fields def form_valid(self, form): # Save the Post without committing to the database yet post = form.save(commit=False) post.author = self.request.user post.save() # Now the Post has an ID to link Skills to # Process the skill input skills_input = form.cleaned_data.get('skills') if skills_input: # Split by newlines, strip whitespace, and skip empty lines skill_list = [skill.strip() for skill in skills_input.split('\n') if skill.strip()] for skill_text in skill_list: Skill.objects.create(element=skill_text, post=post) return super().form_valid(form)
Your existing post_form.html works as-is—crispy forms will automatically render the new skills text field we added to the custom form:
{% extends "taskflow/base.html" %} {% load crispy_forms_tags %} {% block content %} <div class="content-section"> <form method="POST"> {% csrf_token %} <fieldset class="form-group"> <legend class="border-bottom mb-4">Client</legend> {{ form|crispy }} </fieldset> <div class="form-group"> <button class="btn btn-outline-info" type="submit">Save</button> </div> </form> </div> {% endblock content %}
By putting ForeignKey(Skills, ...) on the Post model, you told Django: each Post belongs to one Skill (meaning one Skill can be linked to many Posts). Django's default form for this relationship renders a dropdown to select an existing Skill—not a text input for new skills. That's why you saw an empty dropdown (no Skills existed yet) and couldn't input new skills directly.
Later, on your client detail page (post-detail), you can display all linked skills like this:
{% for skill in post.skills.all %} <span class="badge bg-secondary">{{ skill.element }}</span> {% endfor %}
内容的提问来源于stack exchange,提问作者Silfrid

