diff --git a/djblets/conditions/choices.py b/djblets/conditions/choices.py
index 42c56530f347f195cb3dfe87d64e30eb24cd05a2..d44aa04ace0bc6808981414d6d4d5f7a8f8be129 100644
--- a/djblets/conditions/choices.py
+++ b/djblets/conditions/choices.py
@@ -74,6 +74,19 @@ class BaseConditionChoice(object):
     #: If it's a function, it must accept a ``**kwargs``, for future expansion.
     default_value_field = None
 
+    #: The keyword argument required for condition matching.
+    #:
+    #: This is the name of the keyword argument that must be provided in order
+    #: for this choice to be considered for matching. By default, this expects
+    #: a ``value=`` keyword argument to be passed to
+    #: :py:meth:`Condition.match
+    #: <djblets.conditions.conditions.Condition.match>`, but choices can
+    #: specify another name instead.
+    #:
+    #: This allows multiple choices with different expected values to be used
+    #: in the same :py:class:`~djblets.conditions.conditions.ConditionSet`.
+    value_kwarg = 'value'
+
     def __init__(self, **kwargs):
         """Initialize the condition choice.
 
diff --git a/djblets/conditions/conditions.py b/djblets/conditions/conditions.py
index d304f8239819955894435a5ab7d9e7ebadf88f2c..b3d937a2fa363c319a8f86b5fbc16051b29a534c 100644
--- a/djblets/conditions/conditions.py
+++ b/djblets/conditions/conditions.py
@@ -328,15 +328,18 @@ class ConditionSet(object):
         self.mode = mode
         self.conditions = conditions
 
-    def matches(self, value):
+    def matches(self, **values):
         """Check if a value matches the condition set.
 
         Depending on the mode of the condition set, this will either require
         all conditions to match, or only one.
 
         Args:
-            value (object):
-                The value to match against.
+            **values (dict):
+                Values to match against. By default, condition choices
+                will match against a single ``value`` keyword argument, but
+                more specialized uses might take into account one or more
+                other keyword arguments.
 
         Returns:
             bool:
@@ -344,20 +347,16 @@ class ConditionSet(object):
             does not.
         """
         if self.mode == self.MODE_ALL:
-            match_conditions = all
+            match_conditions = self._match_all
         elif self.mode == self.MODE_ANY:
             match_conditions = any
         else:
             # We shouldn't be here, unless someone set the mode to a bad value
             # after creating the condition set.
-            assert False
-
-        value_state_cache = {}
+            raise ValueError('Invalid condition mode %r' % self.mode)
 
-        return match_conditions(
-            condition.matches(value, value_state_cache=value_state_cache)
-            for condition in self.conditions
-        )
+        return match_conditions(self._get_condition_results(self.conditions,
+                                                            values))
 
     def serialize(self):
         """Serialize the condition set to a JSON-serializable dictionary.
@@ -374,3 +373,59 @@ class ConditionSet(object):
                 for condition in self.conditions
             ],
         }
+
+    def _get_condition_results(self, conditions, values):
+        """Yield the results from each condition match.
+
+        This will iterate through all the conditions, running a match against
+        the provided values, yielding each result.
+
+        If a condition expects a particular value that's not provided in
+        ``values``, it will evaluate as a false match.
+
+        Args:
+            conditions (list of djblets.conditions.condition.Condition):
+                The conditions to iterate through.
+
+            values (dict):
+                The dictionary of values to match against.
+
+        Yields:
+            bool:
+            The result of each condition match.
+        """
+        value_state_cache = {}
+
+        for condition in conditions:
+            value_kwarg = condition.choice.value_kwarg
+
+            if value_kwarg in values:
+                yield condition.matches(values[value_kwarg],
+                                        value_state_cache=value_state_cache)
+            else:
+                yield False
+
+    def _match_all(self, results):
+        """Return whether all results are truthy and the list is non-empty.
+
+        This works similarly to :py:func:`all`, but will return ``False`` if
+        the provided list is empty.
+
+        Args:
+            results (generator):
+                The condition results to iterate through.
+
+        Returns:
+            bool:
+            ``True`` if there are condition results present and they all
+            evaluate to ``True``. ``False`` otherwise.
+        """
+        found = False
+
+        for result in results:
+            if not result:
+                return False
+
+            found = True
+
+        return found
diff --git a/djblets/conditions/tests/test_conditions.py b/djblets/conditions/tests/test_conditions.py
index c901e9a2cdf9348ec459a83c81c86617e7409fc6..1dc438fa21a8923373b9ef644a0b984c32b2227d 100644
--- a/djblets/conditions/tests/test_conditions.py
+++ b/djblets/conditions/tests/test_conditions.py
@@ -384,7 +384,7 @@ class ConditionSetTests(TestCase):
             Condition(choice, choice.get_operator('equals-test-op'), 'abc123'),
         ])
 
-        self.assertTrue(condition_set.matches('abc123'))
+        self.assertTrue(condition_set.matches(value='abc123'))
 
     def test_matches_with_all_mode_and_no_match(self):
         """Testing ConditionSet.matches with "all" mode and no match"""
@@ -395,7 +395,7 @@ class ConditionSetTests(TestCase):
             Condition(choice, choice.get_operator('equals-test-op'), 'def123'),
         ])
 
-        self.assertFalse(condition_set.matches('abc123'))
+        self.assertFalse(condition_set.matches(value='abc123'))
 
     def test_matches_with_any_mode_and_match(self):
         """Testing ConditionSet.matches with "any" mode and match"""
@@ -406,7 +406,7 @@ class ConditionSetTests(TestCase):
             Condition(choice, choice.get_operator('equals-test-op'), 'def123'),
         ])
 
-        self.assertTrue(condition_set.matches('abc123'))
+        self.assertTrue(condition_set.matches(value='abc123'))
 
     def test_matches_with_any_mode_and_no_match(self):
         """Testing ConditionSet.matches with "any" mode and no match"""
@@ -417,7 +417,78 @@ class ConditionSetTests(TestCase):
             Condition(choice, choice.get_operator('equals-test-op'), 'def123'),
         ])
 
-        self.assertFalse(condition_set.matches('foo'))
+        self.assertFalse(condition_set.matches(value='foo'))
+
+    def test_matches_with_custom_value_kwargs(self):
+        """Testing ConditionSet.matches with custom value keyword arguments"""
+        class CustomEqualsChoice(EqualsTestChoice):
+            value_kwarg = 'my_value'
+
+        choice = CustomEqualsChoice()
+
+        condition_set = ConditionSet(ConditionSet.MODE_ALL, [
+            Condition(choice, choice.get_operator('equals-test-op'),
+                      'abc123'),
+        ])
+
+        self.assertTrue(condition_set.matches(my_value='abc123'))
+        self.assertFalse(condition_set.matches(value='abc123'))
+
+    def test_matches_with_all_mode_and_custom_value_kwargs_multiple(self):
+        """Testing ConditionSet.matches with "all" mode and multiple custom
+        value keyword arguments across multiple choices
+        """
+        class CustomEqualsChoice1(EqualsTestChoice):
+            value_kwarg = 'my_value1'
+
+        class CustomEqualsChoice2(EqualsTestChoice):
+            value_kwarg = 'my_value2'
+
+        choice1 = CustomEqualsChoice1()
+        choice2 = CustomEqualsChoice2()
+
+        condition_set = ConditionSet(ConditionSet.MODE_ALL, [
+            Condition(choice1, choice1.get_operator('equals-test-op'),
+                      'abc123'),
+            Condition(choice2, choice2.get_operator('equals-test-op'),
+                      'def456'),
+        ])
+
+        self.assertTrue(condition_set.matches(my_value1='abc123',
+                                              my_value2='def456'))
+        self.assertFalse(condition_set.matches(my_value1='abc123'))
+        self.assertFalse(condition_set.matches(my_value2='def456'))
+        self.assertFalse(condition_set.matches(my_value1='abc123',
+                                               my_value2='xxx'))
+
+    def test_matches_with_any_mode_and_custom_value_kwargs_multiple(self):
+        """Testing ConditionSet.matches with "any" mode and multiple custom
+        value keyword arguments across multiple choices
+        """
+        class CustomEqualsChoice1(EqualsTestChoice):
+            value_kwarg = 'my_value1'
+
+        class CustomEqualsChoice2(EqualsTestChoice):
+            value_kwarg = 'my_value2'
+
+        choice1 = CustomEqualsChoice1()
+        choice2 = CustomEqualsChoice2()
+
+        condition_set = ConditionSet(ConditionSet.MODE_ANY, [
+            Condition(choice1, choice1.get_operator('equals-test-op'),
+                      'abc123'),
+            Condition(choice2, choice2.get_operator('equals-test-op'),
+                      'def456'),
+        ])
+
+        self.assertTrue(condition_set.matches(my_value1='abc123',
+                                              my_value2='def456'))
+        self.assertTrue(condition_set.matches(my_value1='abc123'))
+        self.assertTrue(condition_set.matches(my_value2='def456'))
+        self.assertTrue(condition_set.matches(my_value1='abc123',
+                                              my_value2='xxx'))
+        self.assertFalse(condition_set.matches(my_value1='xxx',
+                                               my_value2='xxx'))
 
     def test_serialize(self):
         """Testing ConditionSet.serialize"""
