Exemple #1
0
def add_favorite_user(request, user_id=None):
    """
    指定されたIDのユーザをログインユーザのフェイバリットに登録します。
    指定IDのユーザを除くユーザだけが作成を行うことができます。(user!=request.user)
    
    JSONで返されるFavoriteUserインスタンスは以下の状態になっています。
    user: ログインユーザ
    target: 指定されたIDのUserインスタンス
    
    @param user_id: ユーザID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定されたユーザがログインユーザの場合)
    @return: 404レスポンス (指定されたIDのユーザが存在しない場合)
    @return: 200レスポンス (成功。FavoriteUserインスタンスをJSONで出力)
    """
    target = get_object_or_404(User, pk=user_id)
    user_is_active_or_404(target)
    if target == request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    if not FavoriteUser.objects.reach_limit(request.user):
        try:
            fav = FavoriteUser.objects.get(user=request.user, target=target)
        except:
            fav = FavoriteUser.objects.create(user=request.user, target=target)
        data = serializers.serialize("json", [fav])
        return HttpResponse(data, mimetype="application/javascript")
    else:
        message = u"申し訳ありません。フェイバリットメンバーにできるのは%s人までです。" % FavoriteUser.objects.limit
        request.user.message_set.create(message=message)
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
Exemple #2
0
def register_direction(request, recipe_id=None):
    """
    指定されたIDのレシピに関する作り方データを新規作成します。
    レシピを作成したユーザだけが作成を行うことができます。(recipe.user==request.user)
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザではない場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 200レスポンス (成功。DirectionインスタンスをJSONで出力)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if recipe.user != request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    form = forms.DirectionForm(request.POST, request.FILES)
    if not form.is_valid():
        json = simplejson.dumps({"status":"error", "message":"form is not valid."})
        direction = None
    else:
        direction = form.save(commit=False)
        recipe.add_direction(direction)
        direction.save()
        d = {"status":"success",
             "message":"",
             "direction":{"pk":direction.id, 
                          "fields":{"text":direction.text,
                                    "photo":direction.photo and direction.photo.url or ""}}}
        json = simplejson.dumps(d)
    if direction.photo:
        data = "<textarea>%s</textarea>" % json
    else:
        data = json
    return HttpResponse(data, mimetype="text/html")
Exemple #3
0
def submit_recipe_to_contest(request, recipe_id):
    """
    指定されたIDのレシピをPOSTのcontestの値で指定されたお題に投稿します。
    レシピの作成ユーザだけが行うことができます。
    成功後のレシピは以下の状態になります。
    contest: 指定されたIDのContestインスタンス
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 403レスポンス (ログインユーザが作成者ではない場合)
    @return: 403レスポンス (TODO: 指定のお題が存在しない場合)
    @return: 200レスポンス (レシピのJSONデータを返す。成功した場合)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if request.user != recipe.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    contest_id = request.POST.get("contest", None)
    if contest_id:
        contest = Contest.objects.get(pk=contest_id)
        recipe.contest = contest
        recipe.save()
        json = serializers.serialize("json", [contest])
    else:
        json = ""
    return HttpResponse(json, mimetype="application/json")
Exemple #4
0
def sort_directions(request, recipe_id=None):
    """
    指定されたIDのレシピにひもづく作り方データを並べ替えます。
    レシピを作成したユーザだけが行うことができます。(recipe.user==request.user)
    POSTのdirection[]の値リストに含まれる、作り方IDの順序通りになります。
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザではない場合)
    @return: 200レスポンス (成功。DirectionインスタンスをJSONで出力)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if recipe.user != request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    direction_ids = request.POST.getlist("direction[]")
    direction_dict = {}
    for direction in recipe.direction_set.all():
        direction_dict[direction.id] = direction
    for i, direction_id in enumerate(direction_ids):
        direction = direction_dict[int(direction_id)]
        direction.number = i
        direction.save()
    json = simplejson.dumps(direction_ids)
    return HttpResponse(json, mimetype="application/json")
Exemple #5
0
def edit_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピデータの変更を行います。
    レシピを作成したユーザだけが変更を行うことができます。(recipe.user==request.user)
    forms.RecipeFormで定義された値を受け取ります。
    また、複数の素材データ(食材:foodパラメータのリストと分量:quantityパラメータのリスト)を
    JSON文字列にエンコードします。
    素材データはrecipe.decode_ingredients()でタプル(食材名,分量)のリストに
    復元することができます。
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザではない場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 302レスポンス (レシピ閲覧ページへ。成功した場合)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if request.user != recipe.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    recipe.encode_ingredients(request.POST.getlist("food"), 
        request.POST.getlist("quantity"))
    form = forms.RecipeForm(request.POST, request.FILES, instance=recipe)
    if form.is_valid():
        recipe = form.save()
        request.user.message_set.create(message=u"レシピを保存しました。")
        return HttpResponseRedirect(reverse("recipes-show",
                                            kwargs={"recipe_id":recipe.id}))
    else:
        return render_edit_recipe_page(request, recipe, form)
Exemple #6
0
def validate(request, key=None):
    """
    ユーザをアクティベートします。
    指定されたkeyを持つUserProfileインスタンスを検索し、そのuserインスタンスの
    is_activeをTrueに設定します。
    UserProfileインスタンスのkey_issued_atが2日以上古い場合、403エラーを返します。
    
    @context created_user: アクティベートされたユーザ
    @context profile: ユーザプロファイル
    @param key: バリデーションキー
    @return: 200レスポンス (成功。)
    @return: 403レスポンス (UserProfileインスタンスのkey_issued_atが2日以上古い場合)
    @return: 404レスポンス (該当のUserProfileインスタンスが存在しない場合)
    """
    profile = get_object_or_404(UserProfile, validation_key=key)
    if not profile.validate(key):
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    profile.disable_validation_key()
    profile.save()
    user = profile.user
    user.is_active = True
    user.save()
    d = {"created_user":user, "profile":profile}
    return render_to_response("registration/new_success.html", 
        d, RequestContext(request))
Exemple #7
0
def show_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピ詳細ページを出力します。
    レシピがis_draft == Trueの場合、作成したユーザのみ見ることができます。
    
    @param recipe_id: RecipeインスタンスのID
    @context recipe: Recipeインスタンス 
                     (recipe.id == recipe_id)
    @return: 403レスポンス (recipe.is_draft == True and 
                         request.user != recipe.user の場合)
    @return: 404レスポンス (指定されたIDのRecipeインスタンスが存在しない場合)
    @return: 404レスポンス (指定レシピの作成ユーザがis_active=Falseの場合
                         ただしレシピが商品化決定している場合を除く)
    @return: 200レスポンス (成功。詳細ページを表示)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    recipe_user = recipe.user
    if not recipe.is_awarded:
        user_is_active_or_404(recipe_user)
    if recipe.is_draft and request.user != recipe_user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    recipe.set_request_user(request.user)
    comment_form = None
    profile = recipe_user.get_profile()
    if not profile.deny_comment:
        comment_form = forms.CommentForm()
    submit_form = forms.SubmitToContestForm()
    d = {"recipe":recipe, "comment_form":comment_form,
         "submit_form":submit_form}
    return render_to_response("recipes/recipe.html",
        d, RequestContext(request))
Exemple #8
0
def delete_direction(request, recipe_id=None, direction_id=None):
    """
    指定されたIDの作り方データを削除します。
    レシピを作成したユーザだけが行うことができます。(recipe.user==request.user)
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザではない場合)
    @return: 403レスポンス (指定の作り方が指定のレシピにひもづいていない場合)
    @return: 200レスポンス (成功。DirectionインスタンスをJSONで出力)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if recipe.user != request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    direction = get_object_or_404(Direction, pk=direction_id)
    if recipe != direction.recipe:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    json = serializers.serialize("json", [direction])
    direction.delete();
    return HttpResponse(json, mimetype="application/json")
Exemple #9
0
def show_recipe_for_print(request, recipe_id=None):
    """
    指定されたIDのレシピ詳細ページ(印刷向け)を出力します。
    条件はshow_recipeと同じです。
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if not recipe.is_awarded:
        user_is_active_or_404(recipe.user)
    if recipe.is_draft and request.user != recipe.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    recipe.set_request_user(request.user)
    d = {"recipe":recipe}
    return render_to_response("recipes/recipe_print.html",
        d, RequestContext(request))
Exemple #10
0
def vote_to_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピに投票します。
    レシピ作成者ではないユーザだけが行うことができます。
    
    @param recipe_id: レシピID
    @return: 403レスポンス (ログインユーザがレシピの作成者である場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合、または作成者がis_active=Falseの場合)
    @return: 200レスポンス (成功。VoteインスタンスをJSONで出力)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    user_is_active_or_404(recipe.user)
    if recipe.user == request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    vote = recipe.vote(request.user)
    recipe.save()
    data = serializers.serialize("json", [vote]) if vote else '{}'
    return HttpResponse(data, mimetype="application/json")
Exemple #11
0
def approve_comment(request, comment_id):
    """
    コメントを承認します。これにより他のユーザにコメントが見えるようになります。
    承認できるのはコメント先のレシピを作成したユーザだけです。
    
    @param comment_id: コメントID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (ログインユーザがレシピの作成者でない場合)
    @return: 404レスポンス (指定されたIDのコメントが存在しない場合)
    @return: 200レスポンス (成功。コメントのJSONデータを返す)
    """
    comment = get_object_or_404(Comment, pk=comment_id)
    if request.user != comment.owner:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    comment.approve()
    comment.save()
    json = serializers.serialize("json", [comment])
    return HttpResponse(json, mimetype="application/json")
Exemple #12
0
def comment_to_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピにコメントします。
    ログインユーザだけが行うことができます。
    POSTする項目はCommentFormの定義に従います。
    作成成功時、ログインユーザがレシピの作成者の場合は、is_moderatedはTrueになります。
    それ以外の場合はFalseになり、レシピ閲覧画面に表示されません。
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定レシピの作成者がprofile.deny_comment == Trueの場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合、または作成者がis_active=Falseの場合)
    @return: 200レスポンス (not form.is_valid()の場合、フォームを再表示)
    @return: 302レスポンス (成功。レシピ閲覧ページにリダイレクトする)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    recipe_user = recipe.user
    user_is_active_or_404(recipe_user)
    profile = recipe_user.get_profile()
    if profile.deny_comment:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    form = forms.CommentForm(request.POST, request.FILES)
    if not form.is_valid():
        message = u"コメント内容に問題があります。"
        request.user.message_set.create(message=message)
        d = {"recipe":recipe, "comment_form":form,
             "submit_form":forms.SubmitToContestForm()}
        return render_to_response("recipes/recipe.html",
            d, RequestContext(request))
    comment = form.save(commit=False)
    comment.user = request.user
    comment.recipe = recipe
    comment.owner = recipe_user
    if recipe_user == request.user:
        comment.approve()
        message = u"コメントが登録されました。"
    else:
        message = u"コメントを受け付けました。%s さんが承認すると表示されます。" % recipe_user.first_name
    comment.save()
    request.user.message_set.create(message=message)
    return HttpResponseRedirect(reverse("recipes-show", 
                                        kwargs={"recipe_id":recipe.id}))
Exemple #13
0
def edit_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピの編集画面を表示します。
    レシピを作成したユーザだけが表示を行うことができます。(recipe.user==request.user)
    
    @param recipe_id: レシピID
    @context recipe: Recipeインスタンス
    @context directions: クエリセット。recipe.direction_set.all()
    @context form: RecipeFormインスタンス
    @context direction_form: DirectionFormインスタンス
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザではない場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 200レスポンス (成功。フォームを表示)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if request.user != recipe.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    form = forms.RecipeForm(instance=recipe, editing=True)
    return render_edit_recipe_page(request, recipe, form)
Exemple #14
0
def comment_to_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピにコメントします。
    POSTする項目はCommentFormの定義に従います。
    作成成功時、is_moderatedはTrueになります。
    
    @param recipe_id: レシピID
    @return: 403レスポンス (指定レシピの作成者がprofile.deny_comment == Trueの場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合、または作成者がis_active=Falseの場合)
    @return: 200レスポンス (not form.is_valid()の場合、フォームを再表示)
    @return: 302レスポンス (成功。レシピ閲覧ページにリダイレクトする)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    recipe_user = recipe.user
    user_is_active_or_404(recipe_user)
    profile = recipe_user.get_profile()
    if profile.deny_comment:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    form = forms.CommentForm(request.POST, request.FILES)
    if not form.is_valid():
        message = u"内容に問題があります。"
        request.user.message_set.create(message=message)
        d = {"recipe":recipe, "comment_form":form,
             "submit_form":forms.SubmitToContestForm()}
        return render_to_response("recipes/recipe.html",
            d, RequestContext(request))
    comment = form.save(commit=False)
    if request.user.is_authenticated():
        comment.user = request.user
    comment.recipe = recipe
    comment.owner = recipe_user
    if recipe_user == request.user:
        comment.approve()
        message = u"写真が登録されました。"
    else:
        message = u"写真が登録されました。"
    comment.save()
    from django.contrib import messages
    messages.add_message(request, messages.INFO, message=message)
    return HttpResponseRedirect(reverse("recipes-show", 
                                        kwargs={"recipe_id":recipe.id}))
Exemple #15
0
def delete_comment(request, comment_id):
    """
    コメントを削除します。
    削除できるのはコメントしたユーザかコメント先のレシピを作成したユーザだけです。
    
    @param comment_id: コメントID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 404レスポンス (指定されたIDのコメントが存在しない場合)
    @return: 403レスポンス (ログインユーザがレシピ、コメントの作成者でない場合)
    @return: 200レスポンス (成功。コメントのJSONデータが返ります)
    """
    comment = get_object_or_404(Comment, pk=comment_id)
    if request.user not in (comment.owner, comment.user):
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    json = serializers.serialize("json", [comment])
    if comment.is_moderated:
        recipe = comment.recipe
        recipe.delete_comment(comment)
        recipe.save()
    comment.delete()
    return HttpResponse(json, mimetype="application/json")
Exemple #16
0
def toggle_recipe_open_state(request, recipe_id=None):
    """
    指定されたIDのレシピの公開状態を変更します。
    レシピが下書き状態(is_draft==True)の場合、公開状態(is_draft==False)に変更します。
    公開状態の場合、下書き状態に変更します。
    レシピを作成したユーザだけが状態の変更を行うことができます。(recipe.user==request.user)
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザではない場合
                          商品化が決定している場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 200レスポンス (JSONが返される。成功した場合)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if request.user != recipe.user or recipe.is_awarded:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    recipe.toggle_open_state()
    recipe.save()
    data = serializers.serialize("json", [recipe]) # TODO: 情報を削減
    return HttpResponse(data, mimetype="application/json")
Exemple #17
0
def delete_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピを削除します。
    レシピを作成したユーザだけが行うことができます。(recipe.user==request.user)
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザではない場合
                         商品化が決定している場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 302レスポンス (POSTのredirect_pathの値、
                         またはsettings.LOGIN_REDIRECT_URLへ。成功した場合)
    """
    redirect_path = request.POST.get("redirect_path", 
                                     settings.LOGIN_REDIRECT_URL)
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if recipe.user != request.user or recipe.is_awarded:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    message = u"%s を削除しました。" % recipe.name
    recipe.delete()
    request.user.message_set.create(message=message)
    return HttpResponseRedirect(redirect_path)
Exemple #18
0
def submit_recipe(request, contest_id=None, recipe_id=None):
    """
    指定されたIDのお題に指定されたIDのレシピを投稿します。
    投稿されたレシピは、recipe.contest = contestとなります。
    
    レシピの作成者でなければ投稿を行うことはできません。
    
    @param contest_id: ContestインスタンスのID
    @param recipe_id: RecipeインスタンスのID
    @return: 200レスポンス (成功。JSONを返す)
    @return: 302レスポンス (ログインしていない場合。ログインページへ)
    @return: 403レスポンス (request.user != recipe.user の場合)
    @return: 404レスポンス (指定されたIDのRecipe, Contestインスタンスが存在しない場合)
    """
    contest = get_object_or_404(Contest, pk=contest_id)
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if recipe.user != request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    recipe.contest = contest
    recipe.save()
    data = serializers.serialize("json", [recipe])
    return HttpResponse(data, mimetype="application/javascript")
Exemple #19
0
def remove_favorite_user(request, user_id=None):
    """
    指定されたIDのユーザをログインユーザのフェイバリットから削除します。
    該当のFavoriteUserインスタンスを作成したユーザだけが削除を行うことが出来ます。
    成功した場合、削除されたFavoriteRecipeインスタンスがJSONで返ります。
    
    @param user_id: ユーザID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (FavoriteUserインスタンスを作成していない場合)
    @return: 404レスポンス (指定されたIDのユーザをフェイバリットにしていない場合)
    @return: 200レスポンス (成功。FavoriteUserインスタンスをJSONで出力)
    """
    target = get_object_or_404(User, pk=user_id)
    if target == request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    try:
        fav = FavoriteUser.objects.get(user=request.user, target=target)
        data = serializers.serialize("json", [fav])
        fav.delete()
    except:
        raise Http404
    return HttpResponse(data, mimetype="application/javascript")
Exemple #20
0
def remove_favorite_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピをログインユーザのフェイバリットから削除します。
    該当のFavoriteRecipeインスタンスを作成したユーザだけが削除を行うことができます。
    成功した場合、削除されたFavoriteRecipeインスタンスがJSONで返ります。
    
    @param recipe_id: レシピID
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (ログインユーザがレシピの作成者である場合)
    @return: 403レスポンス (ログインユーザが該当のFavoriteRecipeインスタンスを作成していない場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合)
    @return: 200レスポンス (成功。FavoriteRecipeインスタンスをJSONで出力)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    if recipe.user == request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    try:
        fav = FavoriteRecipe.objects.get(user=request.user, recipe=recipe)
        data = serializers.serialize("json", [fav])
        fav.delete()
    except:
        raise Http404
    return HttpResponse(data, mimetype="application/json")
Exemple #21
0
def validate_change_email(request, user_id=None, key=None):
    """
    メールアドレス変更を行います。
    
    @param user_id: 変更を行うユーザのID
    @param key: 変更のためのバリデーションキー
    @return: 403レスポンス (キーが正しくない場合)
    @return: 200レスポンス (成功)
    """
    user = get_object_or_404(User, pk=user_id)
    profile = user.get_profile()
    if profile.validate(key):
        profile.disable_validation_key()
        profile.save()
        user.email = profile.pending_email
        user.username = forms.email_to_username(user.email)
        user.save()
        d = {"title":u"メールアドレスの変更完了",
             "text":u"メールアドレスの変更が完了しました。"}
        return render_to_response("base_message.html", 
                                  d, RequestContext(request))
    else:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
Exemple #22
0
def add_favorite_recipe(request, recipe_id=None):
    """
    指定されたIDのレシピをログインユーザのフェイバリットに登録します。
    レシピを作成したユーザを除くユーザだけが作成を行うことができます。(recipe.user!=request.user)
    
    JSONで返されるFavoriteRecipeインスタンスは以下の状態になっています。
    user: ログインユーザ
    recipe: 指定されたIDのRecipeインスタンス
    
    @param recipe_id: レシピID
    @context favorite_recipe: 作成されたFavoriteRecipeインスタンス
    @return: 302レスポンス (ログインページへ。ログインしていない場合)
    @return: 403レスポンス (指定されたレシピの作成者がログインユーザの場合)
    @return: 404レスポンス (指定されたIDのレシピが存在しない場合、または作成者がis_active=Falseの場合)
    @return: 200レスポンス (成功。FavoriteRecipeインスタンスをJSONで出力)
    """
    recipe = get_object_or_404(Recipe, pk=recipe_id)
    user_is_active_or_404(recipe.user)
    if recipe.user == request.user:
        return render_to_response_of_class(HttpResponseForbidden, "403.html")
    fav = recipe.favorite(request.user)
    data = serializers.serialize("json", [fav])
    return HttpResponse(data, mimetype="application/json")