You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中创建模型时生成多模型实例的实现方案求助

问题描述

我想实现的功能:用户在表单输入关键词提交搜索,搜索请求会保存到TweetSearch模型。之后运行get_tweets()函数抓取含该关键词的推文,保存到TweetInstance模型,最后在仪表盘中展示TweetInstance的部分数据。

我不清楚怎么在单个视图里创建多个模型实例,所以在搜索表单和仪表盘之间加了一个视图来运行get_tweets()并存数据,但效果不好,肯定有更优的实现方式。

另一种思路是把这个函数放进dashboard()视图,但这样每次POST请求都会执行该函数,导致网站运行极慢。

求帮助解决这个问题。

我尝试的代码

models.py

from datetime import datetime, timedelta
import uuid
from django.db import models
from django.contrib.auth.models import User

class TweetSearch(models.Model):
    search_term = models.CharField(max_length=200, blank=True)
    QUERY_CHOICES = (
        ('t', 'in tweet'),
        ('h', 'in hashtag'),
        ('u', 'username'),
    )
    id = models.UUIDField(primary_key=True, default=uuid.uuid4)
    query_type = models.CharField(max_length=1, choices=QUERY_CHOICES, blank=True, default='t')
    start_default = datetime.now() - timedelta(days=30)
    start_date = models.DateTimeField(default=start_default)
    end_date = models.DateTimeField(default=datetime.now)
    language = models.CharField(max_length=200, blank=True, null=True)
    country = models.CharField(max_length=200, blank=True, null=True)
    city = models.CharField(max_length=200, blank=True, null=True)
    searcher = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True, null=True)

    def __str__(self):
        return f"Tweets with the word {self.search_term} from {self.start_date} till {self.end_date} written in " \
               f"{self.language} in {self.country}."

class TweetInstance(models.Model):
    tweet_search = models.ForeignKey('TweetSearch', on_delete=models.RESTRICT, null=True)
    content = models.CharField(max_length=280, blank=True, null=True)
    date = models.DateTimeField(blank=True, null=True)
    url = models.CharField(max_length=500, blank=True, null=True)
    tweet_id = models.CharField(max_length=200, blank=True, null=True)
    username = models.CharField(max_length=200, blank=True, null=True)
    followers_count = models.IntegerField(blank=True, null=True)
    reply_count = models.IntegerField(blank=True, null=True)
    retweet_count = models.IntegerField(blank=True, null=True)
    like_count = models.IntegerField(blank=True, null=True)
    quote_count = models.IntegerField(blank=True, null=True)
    language = models.CharField(max_length=200, blank=True, null=True)
    outlinks = models.CharField(max_length=500, blank=True, null=True)
    media = models.CharField(max_length=500, blank=True, null=True)
    retweeted_tweet = models.CharField(max_length=280, blank=True, null=True)
    quoted_tweet = models.CharField(max_length=280, blank=True, null=True)
    in_reply_to_tweet_id = models.CharField(max_length=200, blank=True, null=True)
    in_reply_to_user = models.CharField(max_length=200, blank=True, null=True)
    mentioned_users = models.CharField(max_length=200, blank=True, null=True)
    long = models.FloatField(null=True, blank=True)
    lat = models.FloatField(null=True, blank=True)
    country = models.CharField(max_length=200, blank=True, null=True)
    city = models.CharField(max_length=200, blank=True, null=True)
    hashtags = models.CharField(max_length=200, blank=True, null=True)
    chashtags = models.CharField(max_length=200, blank=True, null=True)

views.py

from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from django.shortcuts import render
import pandas as pd
from .models import TweetSearch, TweetInstance
from .utils import get_tweets  # 假设get_tweets在utils.py中

class TweetCreate(CreateView):
    model = TweetSearch
    form_class = TweetForm
    success_url = reverse_lazy('create_dashboard')

def create_dashboard(request):
    qs = TweetSearch.objects.all()
    search_data = [
        {
            'search_term': x.search_term,
            'created': x.created
        } for x in qs
    ]
    df = pd.DataFrame(search_data)
    df = df.iloc[-1]
    tweets_df = get_tweets(df['search_term'])
    mylist = []
    tweet_search = TweetSearch.objects.latest('created')

    for i in range(0, len(tweets_df['content'])):
        mylist.append(TweetInstance(
            tweet_search=tweet_search,
            date=tweets_df['date'].iloc[i],
            content=tweets_df['content'].iloc[i],
            url=tweets_df['url'].iloc[i],
            username=tweets_df['username'].iloc[i],
            tweet_id=tweets_df['id'].iloc[i],
            followers_count=tweets_df['followers_count'].iloc[i],
            reply_count=tweets_df['reply_count'].iloc[i],
            retweet_count=tweets_df['retweet_count'].iloc[i],
            like_count=tweets_df['like_count'].iloc[i],
            quote_count=tweets_df['quote_count'].iloc[i],
            language=tweets_df['language'].iloc[i],
            outlinks=tweets_df['outlinks'].iloc[i],
            media=tweets_df['media'].iloc[i],
            retweeted_tweet=tweets_df['retweeted_tweet'].iloc[i],
            quoted_tweet=tweets_df['quoted_tweet'].iloc[i],
            in_reply_to_tweet_id=tweets_df['in_reply_to_tweet_id'].iloc[i],
            in_reply_to_user=tweets_df['in_reply_to_user'].iloc[i],
            mentioned_users=tweets_df['mentioned_users'].iloc[i],
            long=tweets_df['long'].iloc[i],
            lat=tweets_df['lat'].iloc[i],
            country=tweets_df['country'].iloc[i],
            city=tweets_df['city'].iloc[i],
            hashtags=tweets_df['hashtags'].iloc[i],
            chashtags=tweets_df['chashtags'].iloc[i])
        )
    TweetInstance.objects.bulk_create(mylist)
    return render(request, 'twitter_sentiment/loading.html')

def dashboard(request):
    # 处理仪表盘逻辑
    pass
解决方案

1. 重写CreateView的form_valid方法(同步方式)

直接在TweetCreate视图内完成TweetSearch实例创建、推文抓取和TweetInstance保存,无需额外视图,避免多次查询最新数据的问题:

class TweetCreate(CreateView):
    model = TweetSearch
    form_class = TweetForm
    success_url = reverse_lazy('dashboard')  # 提交后直接跳转到仪表盘

    def form_valid(self, form):
        # 先保存搜索请求实例
        self.object = form.save()
        # 调用抓取函数,传入搜索实例的所有参数(比如时间范围、语言等)
        tweets_df = get_tweets(
            self.object.search_term,
            start_date=self.object.start_date,
            end_date=self.object.end_date,
            language=self.object.language,
            country=self.object.country
        )
        
        # 批量创建推文实例
        tweet_instances = []
        for _, row in tweets_df.iterrows():
            tweet_instances.append(TweetInstance(
                tweet_search=self.object,
                date=row['date'],
                content=row['content'],
                url=row['url'],
                username=row['username'],
                tweet_id=row['id'],
                followers_count=row['followers_count'],
                reply_count=row['reply_count'],
                retweet_count=row['retweet_count'],
                like_count=row['like_count'],
                quote_count=row['quote_count'],
                language=row['language'],
                outlinks=row['outlinks'],
                media=row['media'],
                retweeted_tweet=row['retweeted_tweet'],
                quoted_tweet=row['quoted_tweet'],
                in_reply_to_tweet_id=row['in_reply_to_tweet_id'],
                in_reply_to_user=row['in_reply_to_user'],
                mentioned_users=row['mentioned_users'],
                long=row['long'],
                lat=row['lat'],
                country=row['country'],
                city=row['city'],
                hashtags=row['hashtags'],
                chashtags=row['chashtags']
            ))
        TweetInstance.objects.bulk_create(tweet_instances)
        return super().form_valid(form)

这种方式逻辑集中,但如果get_tweets()耗时较长,用户会等待页面加载。

2. 异步处理(推荐方案)

用异步任务队列(如django-q、Celery)把推文抓取逻辑放到后台执行,避免阻塞用户请求:

步骤1:创建异步任务(tasks.py)

from django.db import transaction
from .models import TweetSearch, TweetInstance
from .utils import get_tweets

def fetch_and_save_tweets(tweet_search_id):
    # 获取对应的搜索实例
    tweet_search = TweetSearch.objects.get(id=tweet_search_id)
    # 抓取推文
    tweets_df = get_tweets(
        tweet_search.search_term,
        start_date=tweet_search.start_date,
        end_date=tweet_search.end_date,
        language=tweet_search.language,
        country=tweet_search.country
    )
    
    # 事务内批量保存,保证数据一致性
    with transaction.atomic():
        tweet_instances = []
        for _, row in tweets_df.iterrows():
            tweet_instances.append(TweetInstance(
                tweet_search=tweet_search,
                date=row['date'],
                content=row['content'],
                # 其他字段...
            ))
        TweetInstance.objects.bulk_create(tweet_instances)

步骤2:修改TweetCreate视图

from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import TweetSearch
from .tasks import fetch_and_save_tweets
from django_q.tasks import async_task  # 若使用django-q

class TweetCreate(CreateView):
    model = TweetSearch
    form_class = TweetForm
    success_url = reverse_lazy('dashboard')

    def form_valid(self, form):
        self.object = form.save()
        # 触发异步任务,后台执行抓取和保存
        async_task('your_app.tasks.fetch_and_save_tweets', self.object.id)
        return super().form_valid(form)

用户提交表单后会立即跳转到仪表盘,推文抓取在后台运行。可在仪表盘添加状态提示,比如“正在抓取推文,请稍后刷新查看”。

3. 仪表盘优化

只查询当前用户最新搜索对应的推文,避免全量查询:

def dashboard(request):
    # 获取当前用户最近的搜索记录
    latest_search = TweetSearch.objects.filter(searcher=request.user).latest('created')
    # 获取该搜索下的前20条推文
    tweets = TweetInstance.objects.filter(tweet_search=latest_search)[:20]
    context = {
        'latest_search': latest_search,
        'tweets': tweets
    }
    return render(request, 'twitter_sentiment/dashboard.html', context)

内容的提问来源于stack exchange,提问作者JelleHeijne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 02:01:04